diff --git a/Application/Commands/CancelPlayOfferCommand.cs b/Application/Commands/CancelPlayOfferCommand.cs index 2b675bb..6cbf4e5 100644 --- a/Application/Commands/CancelPlayOfferCommand.cs +++ b/Application/Commands/CancelPlayOfferCommand.cs @@ -2,6 +2,6 @@ namespace PlayOfferService.Application.Commands; -public record CancelPlayOfferCommand(Guid PlayOfferId) : IRequest +public record CancelPlayOfferCommand(Guid PlayOfferId, Guid MemberId) : IRequest { } diff --git a/Application/Commands/CreatePlayOfferCommand.cs b/Application/Commands/CreatePlayOfferCommand.cs index 928edc5..618e559 100644 --- a/Application/Commands/CreatePlayOfferCommand.cs +++ b/Application/Commands/CreatePlayOfferCommand.cs @@ -2,6 +2,6 @@ using PlayOfferService.Domain.Models; namespace PlayOfferService.Application.Commands; -public record CreatePlayOfferCommand(PlayOfferDto PlayOfferDto) : IRequest +public record CreatePlayOfferCommand(CreatePlayOfferDto CreatePlayOfferDto, Guid CreatorId, Guid ClubId) : IRequest { } \ No newline at end of file diff --git a/Application/Commands/JoinPlayOfferCommand.cs b/Application/Commands/JoinPlayOfferCommand.cs index 1147257..e52ad8b 100644 --- a/Application/Commands/JoinPlayOfferCommand.cs +++ b/Application/Commands/JoinPlayOfferCommand.cs @@ -2,6 +2,6 @@ using PlayOfferService.Domain.Models; namespace PlayOfferService.Application.Commands; -public record JoinPlayOfferCommand(JoinPlayOfferDto JoinPlayOfferDto) : IRequest +public record JoinPlayOfferCommand(JoinPlayOfferDto JoinPlayOfferDto, Guid MemberId) : IRequest { } \ No newline at end of file diff --git a/Application/Controllers/PlayOfferController.cs b/Application/Controllers/PlayOfferController.cs index 8345b4d..de8e5b9 100644 --- a/Application/Controllers/PlayOfferController.cs +++ b/Application/Controllers/PlayOfferController.cs @@ -1,13 +1,16 @@ +using System.Security.Claims; using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PlayOfferService.Application.Commands; +using PlayOfferService.Application.Exceptions; using PlayOfferService.Application.Queries; using PlayOfferService.Domain.Models; namespace PlayOfferService.Application.Controllers; [ApiController] -[Route("api")] +[Route("api/playoffers")] public class PlayOfferController : ControllerBase { @@ -18,49 +21,114 @@ public PlayOfferController(IMediator mediator) _mediator = mediator; } + /// + /// Retrieve all Play Offers of the logged in users club + /// + /// Play offers with a matching club id + /// Returns a list of Play offers matching the query params + /// No Play offer with matching properties was found + [HttpGet] + [Authorize] + [Route("club")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)] + [Consumes("application/json")] + [Produces("application/json")] + public async Task>> GetByClubIdAsync() + { + var clubId = Guid.Parse(User.Claims.First(c => c.Type == "tennisClubId").Value); + var result = await _mediator.Send(new GetPlayOffersByClubIdQuery(clubId)); + + if (result.Count() == 0) + return NoContent(); + + return Ok(result); + } + /// - ///Retrieve all Play Offers matching the query params + ///Retrieve all Play Offers of a logged in user /// - ///The id of the play offer - ///The id of the creator of the play offer - ///The id of the club of the play offer - ///Play offer with a matching id - ///Returns a Play offer matching the query params + ///List of Play offers with where given member is creator or opponent + ///Returns a list of Play offers matching the query params ///No Play offer with matching properties was found [HttpGet] + [Authorize] + [Route("participant")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)] + [Consumes("application/json")] + [Produces("application/json")] + public async Task>> GetByParticipantIdAsync() + { + var participantId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value); + var result = await _mediator.Send(new GetPlayOffersByParticipantIdQuery(participantId)); + + if (result.Count() == 0) + return NoContent(); + + return Ok(result); + } + + /// + ///Get all Play offers created by a member with a matching name + /// + ///Name of the creator in the format '[FirstName] [LastName]', '[FirstName]' or '[LastName]' + ///A list of Play offers with a matching id + ///Returns a List of Play offers with creator matching the query params + ///No Play offers with matching creator was found + [HttpGet] + [Authorize] + [Route("search")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)] [Consumes("application/json")] [Produces("application/json")] - public async Task>> GetByIdAsync([FromQuery] Guid? playOfferId, [FromQuery] Guid? creatorId, [FromQuery] Guid? clubId) + public async Task>> GetByCreatorNameAsync([FromQuery] string creatorName) { - var result = await _mediator.Send(new GetPlayOffersByIdQuery(playOfferId, creatorId, clubId)); + IEnumerable result; + try + { + result = await _mediator.Send(new GetPlayOffersByCreatorNameQuery(creatorName)); + } + catch (Exception e) + { + return BadRequest(e.Message); + } if (result.Count() == 0) return NoContent(); return Ok(result); } + /// - ///Create a new Play Offer + ///Create a new Play Offer for the logged in user /// - ///The Play Offer to create + ///The Play Offer to create ///The newly created Play offer ///Returns the id of the created Play Offer ///Invalid Play Offer structure + ///Only members can create Play Offers [HttpPost] + [Authorize] [ProducesResponseType(typeof(PlayOffer), StatusCodes.Status201Created)] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)] [Consumes("application/json")] [Produces("application/json")] - public async Task> Create(PlayOfferDto playOfferDto) + public async Task> Create(CreatePlayOfferDto createPlayOfferDto) { + if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER") + return Unauthorized("Only members can create Play Offers!"); + + var creatorId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value); + var clubId = Guid.Parse(User.FindFirst("tennisClubId")!.Value); + Guid result; try { - result = await _mediator.Send(new CreatePlayOfferCommand(playOfferDto)); + result = await _mediator.Send(new CreatePlayOfferCommand(createPlayOfferDto, creatorId, clubId)); } catch (Exception e) { @@ -71,22 +139,33 @@ public async Task> Create(PlayOfferDto playOfferDto) } /// - ///Cancels a Play Offer with a matching id + ///Cancels a Play Offer with a matching id of the logged in user /// ///The id of the Play Offer to cancel ///Nothing ///The Play Offer with the matching id was cancelled ///No Play Offer with matching id found + ///Only creator can cancel Play Offers [HttpDelete] + [Authorize] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)] [Consumes("application/json")] [Produces("application/json")] public async Task Delete(Guid playOfferId) { + if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER") + return Unauthorized("Only members can cancel Play Offers!"); + + var memberId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value); + try { - await _mediator.Send(new CancelPlayOfferCommand(playOfferId)); + await _mediator.Send(new CancelPlayOfferCommand(playOfferId, memberId)); + } + catch (AuthorizationException e) + { + return Unauthorized(e.Message); } catch (Exception e) { @@ -97,23 +176,29 @@ public async Task Delete(Guid playOfferId) } /// - ///Adds a given opponentId to a Play Offer and creates a reservation + ///Logged in user joins a Play Offer with a matching playOfferId /// ///The opponentId to add to the Play Offer with the matching playOfferId ///Nothing ///The opponentId was added to the Play Offer with the matching playOfferId ///No playOffer with a matching playOfferId found + ///Only members can join Play Offers [HttpPost] - [Route("/join")] + [Authorize] + [Route("join")] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)] [Consumes("application/json")] [Produces("application/json")] public async Task Join(JoinPlayOfferDto joinPlayOfferDto) { + if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER") + return Unauthorized("Only members can join Play Offers!"); + + var memberId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value); try { - await _mediator.Send(new JoinPlayOfferCommand(joinPlayOfferDto)); + await _mediator.Send(new JoinPlayOfferCommand(joinPlayOfferDto, memberId)); } catch (Exception e) { diff --git a/Application/EventParser.cs b/Application/EventParser.cs index 353f814..54f0de4 100644 --- a/Application/EventParser.cs +++ b/Application/EventParser.cs @@ -1,15 +1,30 @@ using System.Text.Json; using System.Text.Json.Nodes; using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Reservation; namespace PlayOfferService.Application; public class EventParser { - public static BaseEvent ParseEvent(JsonNode jsonEvent) + public static T ParseEvent(JsonNode jsonEvent) where T : BaseEvent, new() { - var originalEventData = JsonNode.Parse(jsonEvent["eventData"].GetValue()); + JsonNode? originalEventData = null; + DateTime timestamp = DateTime.UtcNow; + // If eventData is JsonValue (escaped as string), parse it + if (jsonEvent["eventData"] is JsonValue) + { + originalEventData = JsonNode.Parse(jsonEvent["eventData"].GetValue()); + timestamp = DateTime.Parse(jsonEvent["timestamp"].GetValue()).ToUniversalTime(); + } + else // If eventData is already a JsonNode, just use it --> court_service + { + originalEventData = jsonEvent["eventData"]; + timestamp = DateTimeOffset.FromUnixTimeMilliseconds(jsonEvent["timestamp"]["$date"].GetValue()) + .UtcDateTime; + } + // We need to add the eventType to the event data so we can deserialize it correctly // Since the discriminator needs to be at the first position in the JSON object, we need to create a new object // because JsonNode doesn't allow us to insert at the beginning of the object @@ -18,14 +33,22 @@ public static BaseEvent ParseEvent(JsonNode jsonEvent) newEventData["eventType"] = jsonEvent["eventType"].GetValue(); foreach (var kvp in originalEventData.AsObject()) { - newEventData[kvp.Key] = kvp.Value.DeepClone(); + if (kvp.Value is JsonObject && kvp.Value?["$date"] != null) + { + newEventData[kvp.Key] = DateTimeOffset.FromUnixTimeMilliseconds(kvp.Value["$date"] + .GetValue()).UtcDateTime; + } + else + { + newEventData[kvp.Key] = kvp.Value.DeepClone(); + } } - - return new BaseEvent + + return new T { EventId = Guid.Parse(jsonEvent["eventId"].GetValue()), EventType = (EventType)Enum.Parse(typeof(EventType), jsonEvent["eventType"].GetValue()), - Timestamp = DateTime.Parse(jsonEvent["timestamp"].GetValue()).ToUniversalTime(), + Timestamp = timestamp, EntityId = Guid.Parse(jsonEvent["entityId"].GetValue()), EntityType = (EntityType)Enum.Parse(typeof(EntityType), jsonEvent["entityType"].GetValue()), EventData = JsonSerializer.Deserialize(newEventData, JsonSerializerOptions.Default), diff --git a/Application/Exceptions/AuthorizationException.cs b/Application/Exceptions/AuthorizationException.cs new file mode 100644 index 0000000..53774f7 --- /dev/null +++ b/Application/Exceptions/AuthorizationException.cs @@ -0,0 +1,3 @@ +namespace PlayOfferService.Application.Exceptions; + +public class AuthorizationException(string message) : Exception(message); \ No newline at end of file diff --git a/Application/Handlers/CancelPlayOfferHandler.cs b/Application/Handlers/CancelPlayOfferHandler.cs index 8281132..7047cf5 100644 --- a/Application/Handlers/CancelPlayOfferHandler.cs +++ b/Application/Handlers/CancelPlayOfferHandler.cs @@ -1,7 +1,6 @@ using MediatR; using PlayOfferService.Application.Commands; using PlayOfferService.Application.Exceptions; -using PlayOfferService.Domain; using PlayOfferService.Domain.Events; using PlayOfferService.Domain.Events.PlayOffer; using PlayOfferService.Domain.Models; @@ -11,32 +10,38 @@ namespace PlayOfferService.Application.Handlers; public class CancelPlayOfferHandler : IRequestHandler { - private readonly DbWriteContext _context; + private readonly WriteEventRepository _writeEventRepository; private readonly PlayOfferRepository _playOfferRepository; private readonly ClubRepository _clubRepository; - - public CancelPlayOfferHandler(DbWriteContext context, PlayOfferRepository playOfferRepository, ClubRepository clubRepository) + + public CancelPlayOfferHandler(WriteEventRepository writeEventRepository, PlayOfferRepository playOfferRepository, ClubRepository clubRepository) { - _context = context; + _writeEventRepository = writeEventRepository; _playOfferRepository = playOfferRepository; _clubRepository = clubRepository; } public async Task Handle(CancelPlayOfferCommand request, CancellationToken cancellationToken) { + var transaction = _writeEventRepository.StartTransaction(); + var excpectedEventCount = _writeEventRepository.GetEventCount(request.PlayOfferId) + 1; + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(request.PlayOfferId)).FirstOrDefault(); if (existingPlayOffer == null) throw new NotFoundException($"PlayOffer {request.PlayOfferId} not found!"); + if (existingPlayOffer.CreatorId != request.MemberId) + throw new AuthorizationException($"PlayOffer {request.PlayOfferId} can only be cancelled by creator!"); + if (existingPlayOffer.OpponentId != null) throw new InvalidOperationException($"PlayOffer {request.PlayOfferId} is already accepted and cannot be cancelled!"); if (existingPlayOffer.IsCancelled) throw new InvalidOperationException($"PlayOffer {request.PlayOfferId} is already cancelled!"); - + var existingClub = await _clubRepository.GetClubById(existingPlayOffer.ClubId); if (existingClub == null) throw new NotFoundException($"Club {existingPlayOffer.ClubId} not found!"); - - + + switch (existingClub.Status) { case Status.LOCKED: @@ -44,7 +49,7 @@ public async Task Handle(CancelPlayOfferCommand request, CancellationToken case Status.DELETED: throw new InvalidOperationException("Can't cancel PlayOffer in deleted club!"); } - + var domainEvent = new BaseEvent { EntityId = request.PlayOfferId, @@ -54,9 +59,20 @@ public async Task Handle(CancelPlayOfferCommand request, CancellationToken EventData = new PlayOfferCancelledEvent(), Timestamp = DateTime.UtcNow }; + + await _writeEventRepository.AppendEvent(domainEvent); + await _writeEventRepository.Update(); + + + var eventCount = _writeEventRepository.GetEventCount(request.PlayOfferId); + + if (eventCount != excpectedEventCount) + { + transaction.Rollback(); + throw new InvalidOperationException("Concurrent modification detected!"); + } - _context.Events.Add(domainEvent); - await _context.SaveChangesAsync(); + transaction.Commit(); return Task.CompletedTask; } diff --git a/Application/Handlers/CreatePlayOfferHandler.cs b/Application/Handlers/CreatePlayOfferHandler.cs index 1cc137b..b41fd73 100644 --- a/Application/Handlers/CreatePlayOfferHandler.cs +++ b/Application/Handlers/CreatePlayOfferHandler.cs @@ -1,33 +1,34 @@ using MediatR; using PlayOfferService.Application.Commands; using PlayOfferService.Application.Exceptions; -using PlayOfferService.Domain; using PlayOfferService.Domain.Events; using PlayOfferService.Domain.Models; using PlayOfferService.Domain.Repositories; namespace PlayOfferService.Application.Handlers; + public class CreatePlayOfferHandler : IRequestHandler { - - private readonly DbWriteContext _context; private readonly ClubRepository _clubRepository; private readonly MemberRepository _memberRepository; - public CreatePlayOfferHandler(DbWriteContext context, ClubRepository clubRepository, MemberRepository memberRepository) + private readonly WriteEventRepository _writeEventRepository; + + public CreatePlayOfferHandler(WriteEventRepository writeEventRepository, ClubRepository clubRepository, + MemberRepository memberRepository) { - _context = context; + _writeEventRepository = writeEventRepository; _clubRepository = clubRepository; _memberRepository = memberRepository; } public async Task Handle(CreatePlayOfferCommand request, CancellationToken cancellationToken) { - var playOfferDto = request.PlayOfferDto; - - var club = await _clubRepository.GetClubById(playOfferDto.ClubId); - if(club == null) - throw new ArgumentException($"Club {request.PlayOfferDto.ClubId} not found"); + var playOfferDto = request.CreatePlayOfferDto; + + var club = await _clubRepository.GetClubById(request.ClubId); + if (club == null) + throw new NotFoundException($"Club {request.ClubId} not found"); switch (club.Status) { case Status.LOCKED: @@ -35,10 +36,10 @@ public async Task Handle(CreatePlayOfferCommand request, CancellationToken case Status.DELETED: throw new InvalidOperationException("Can't create PlayOffer in deleted club!"); } - - var creator = await _memberRepository.GetMemberById(playOfferDto.CreatorId); - if(creator == null) - throw new NotFoundException($"Member {request.PlayOfferDto.CreatorId} not found!"); + + var creator = await _memberRepository.GetMemberById(request.CreatorId); + if (creator == null) + throw new NotFoundException($"Member {request.CreatorId} not found!"); switch (creator.Status) { case Status.LOCKED: @@ -65,10 +66,22 @@ public async Task Handle(CreatePlayOfferCommand request, CancellationToken Timestamp = DateTime.Now.ToUniversalTime() }; - _context.Events.Add(domainEvent); - await _context.SaveChangesAsync(); + var transaction = _writeEventRepository.StartTransaction(); + var excpectedEventCount = _writeEventRepository.GetEventCount(playOfferId) + 1; + + await _writeEventRepository.AppendEvent(domainEvent); + await _writeEventRepository.Update(); + + var eventCount = _writeEventRepository.GetEventCount(playOfferId); + + if (eventCount != excpectedEventCount) + { + transaction.Rollback(); + throw new InvalidOperationException("Concurrent modification detected!"); + } + + transaction.Commit(); return playOfferId; } - -} +} \ No newline at end of file diff --git a/Application/Handlers/Events/ClubEventHandler.cs b/Application/Handlers/Events/ClubEventHandler.cs new file mode 100644 index 0000000..f530869 --- /dev/null +++ b/Application/Handlers/Events/ClubEventHandler.cs @@ -0,0 +1,116 @@ +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class ClubEventHandler : IRequestHandler +{ + private readonly ClubRepository _clubRepository; + private readonly ReadEventRepository _readEventRepository; + private readonly WriteEventRepository _writeEventRepository; + private readonly PlayOfferRepository _playOfferRepository; + + public ClubEventHandler(ClubRepository clubRepository, ReadEventRepository readEventRepository, WriteEventRepository writeEventRepository, PlayOfferRepository playOfferRepository) + { + _clubRepository = clubRepository; + _readEventRepository = readEventRepository; + _writeEventRepository = writeEventRepository; + _playOfferRepository = playOfferRepository; + } + + public async Task Handle(TechnicalClubEvent clubEvent, CancellationToken cancellationToken) + { + Console.WriteLine("ClubEventHandler received event: " + clubEvent.EventType); + var existingEvent = await _readEventRepository.GetEventById(clubEvent.EventId); + if (existingEvent != null) + { + Console.WriteLine("Event already applied, skipping"); + return; + } + + switch (clubEvent.EventType) + { + case EventType.TENNIS_CLUB_REGISTERED: + await HandleTennisClubRegisteredEvent(clubEvent); + break; + case EventType.TENNIS_CLUB_LOCKED: + await HandleTennisClubLockedEvent(clubEvent); + break; + case EventType.TENNIS_CLUB_UNLOCKED: + await HandleTennisClubUnlockedEvent(clubEvent); + break; + case EventType.TENNIS_CLUB_DELETED: + await HandleTennisClubDeletedEvent(clubEvent); + break; + case EventType.TENNIS_CLUB_NAME_CHANGED: + await HandleTennisClubNameChangedEvent(clubEvent); + break; + } + + await _clubRepository.Update(); + await _readEventRepository.AppendEvent(clubEvent); + await _readEventRepository.Update(); + } + + private async Task HandleTennisClubDeletedEvent(TechnicalClubEvent clubEvent) + { + await CreatePlayOfferCancelledEventsByClubId(clubEvent); + + var existingClub = await _clubRepository.GetClubById(clubEvent.EntityId); + existingClub!.Apply([clubEvent]); + } + + private async Task HandleTennisClubUnlockedEvent(TechnicalClubEvent clubEvent) + { + var existingClub = await _clubRepository.GetClubById(clubEvent.EntityId); + existingClub!.Apply([clubEvent]); + } + + private async Task HandleTennisClubLockedEvent(TechnicalClubEvent clubEvent) + { + await CreatePlayOfferCancelledEventsByClubId(clubEvent); + + var existingClub = await _clubRepository.GetClubById(clubEvent.EntityId); + existingClub!.Apply([clubEvent]); + } + + private async Task HandleTennisClubRegisteredEvent(TechnicalClubEvent clubEvent) + { + var newClub = new Club(); + newClub.Apply([clubEvent]); + _clubRepository.CreateClub(newClub); + } + + private async Task CreatePlayOfferCancelledEventsByClubId(TechnicalClubEvent clubEvent) + { + // Get all play offers by club id + var existingPlayOffer = await _playOfferRepository.GetPlayOffersByIds(null, null, clubEvent.EntityId); + + // Create PlayOfferCancelled events for each play offer + foreach (var playOffer in existingPlayOffer) + { + var cancelledEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_CANCELLED, + EntityType = EntityType.PLAYOFFER, + EntityId = playOffer.Id, + Timestamp = DateTime.UtcNow, + EventData = new PlayOfferCancelledEvent(), + CorrelationId = clubEvent.EventId + }; + + await _writeEventRepository.AppendEvent(cancelledEvent); + } + await _writeEventRepository.Update(); + } + + private async Task HandleTennisClubNameChangedEvent(TechnicalClubEvent clubEvent) + { + var existingClub = await _clubRepository.GetClubById(clubEvent.EntityId); + existingClub!.Apply([clubEvent]); + } +} \ No newline at end of file diff --git a/Application/Handlers/Events/CourtEventHandler.cs b/Application/Handlers/Events/CourtEventHandler.cs new file mode 100644 index 0000000..51ab458 --- /dev/null +++ b/Application/Handlers/Events/CourtEventHandler.cs @@ -0,0 +1,57 @@ +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Court; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class CourtEventHandler : IRequestHandler +{ + private readonly CourtRepository _courtRepository; + private readonly ReadEventRepository _eventRepository; + + public CourtEventHandler(CourtRepository courtRepository, ReadEventRepository eventRepository) + { + _courtRepository = courtRepository; + _eventRepository = eventRepository; + } + + public async Task Handle(TechnicalCourtEvent courtEvent, CancellationToken cancellationToken) + { + Console.WriteLine("ReservationEventHandler received event: " + courtEvent.EventType); + var existingEvent = await _eventRepository.GetEventById(courtEvent.EventId); + if (existingEvent != null) + { + Console.WriteLine("Event already applied, skipping"); + return; + } + + switch (courtEvent.EventType) + { + case EventType.CourtCreatedEvent: + await HandleCourtCreatedEvent(courtEvent); + break; + case EventType.CourtUpdatedEvent: + await HandleCourtUpdatedEvent(courtEvent); + break; + } + + await _courtRepository.Update(); + await _eventRepository.AppendEvent(courtEvent); + await _eventRepository.Update(); + } + + private async Task HandleCourtUpdatedEvent(TechnicalCourtEvent courtEvent) + { + var existingCourt = await _courtRepository.GetCourtById(courtEvent.EntityId); + existingCourt!.Apply([courtEvent]); + } + + private async Task HandleCourtCreatedEvent(TechnicalCourtEvent courtEvent) + { + var court = new Court(); + court.Apply([courtEvent]); + _courtRepository.CreateCourt(court); + } +} \ No newline at end of file diff --git a/Application/Handlers/Events/MemberEventHandler.cs b/Application/Handlers/Events/MemberEventHandler.cs new file mode 100644 index 0000000..44effb5 --- /dev/null +++ b/Application/Handlers/Events/MemberEventHandler.cs @@ -0,0 +1,126 @@ +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Member; +using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class MemberEventHandler : IRequestHandler +{ + private readonly MemberRepository _memberRepository; + private readonly ReadEventRepository _eventRepository; + private readonly WriteEventRepository _writeEventRepository; + private readonly PlayOfferRepository _playOfferRepository; + + public MemberEventHandler(MemberRepository memberRepository, ReadEventRepository eventRepository, PlayOfferRepository playOfferRepository, WriteEventRepository writeEventRepository) + { + _memberRepository = memberRepository; + _eventRepository = eventRepository; + _playOfferRepository = playOfferRepository; + _writeEventRepository = writeEventRepository; + } + + public async Task Handle(TechnicalMemberEvent memberEvent, CancellationToken cancellationToken) + { + Console.WriteLine("MemberEventHandler received event: " + memberEvent.EventType); + var existingEvent = await _eventRepository.GetEventById(memberEvent.EventId); + if (existingEvent != null) + { + Console.WriteLine("Event already applied, skipping"); + return; + } + + switch (memberEvent.EventType) + { + case EventType.MEMBER_REGISTERED: + await HandleMemberRegisteredEvent(memberEvent); + break; + case EventType.MEMBER_LOCKED: + await HandleMemberLockedEvent(memberEvent); + break; + case EventType.MEMBER_UNLOCKED: + await HandleMemberUnlockedEvent(memberEvent); + break; + case EventType.MEMBER_DELETED: + await HandleMemberDeletedEvent(memberEvent); + break; + case EventType.MEMBER_FULL_NAME_CHANGED: + await HandleMemberFullNameChangedEvent(memberEvent); + break; + case EventType.MEMBER_EMAIL_CHANGED: + await HandleMemberEmailChangedEvent(memberEvent); + break; + } + + await _memberRepository.Update(); + await _eventRepository.AppendEvent(memberEvent); + await _eventRepository.Update(); + } + + private async Task HandleMemberDeletedEvent(TechnicalMemberEvent memberEvent) + { + await CreatePlayOfferCancelledEventsByCreatorId(memberEvent); + + var existingMember = await _memberRepository.GetMemberById(memberEvent.EntityId); + existingMember!.Apply([memberEvent]); + } + + private async Task HandleMemberUnlockedEvent(TechnicalMemberEvent memberEvent) + { + var existingMember = await _memberRepository.GetMemberById(memberEvent.EntityId); + existingMember!.Apply([memberEvent]); + } + + private async Task HandleMemberLockedEvent(TechnicalMemberEvent memberEvent) + { + await CreatePlayOfferCancelledEventsByCreatorId(memberEvent); + + var existingMember = await _memberRepository.GetMemberById(memberEvent.EntityId); + existingMember!.Apply([memberEvent]); + } + + private async Task HandleMemberRegisteredEvent(TechnicalMemberEvent memberEvent) + { + var member = new Member(); + member.Apply([memberEvent]); + _memberRepository.CreateMember(member); + } + + private async Task CreatePlayOfferCancelledEventsByCreatorId(TechnicalMemberEvent memberEvent) + { + // Get all play offers by creator id + var existingPlayOffer = await _playOfferRepository.GetPlayOffersByIds(null, memberEvent.EntityId); + + // Create PlayOfferCancelled events for each play offer + foreach (var playOffer in existingPlayOffer) + { + var cancelledEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_CANCELLED, + EntityType = EntityType.PLAYOFFER, + EntityId = playOffer.Id, + Timestamp = DateTime.UtcNow, + EventData = new PlayOfferCancelledEvent(), + CorrelationId = memberEvent.EventId + }; + + await _writeEventRepository.AppendEvent(cancelledEvent); + } + await _writeEventRepository.Update(); + } + + private async Task HandleMemberFullNameChangedEvent(TechnicalMemberEvent memberEvent) + { + var existingMember = await _memberRepository.GetMemberById(memberEvent.EntityId); + existingMember!.Apply([memberEvent]); + } + + private async Task HandleMemberEmailChangedEvent(TechnicalMemberEvent memberEvent) + { + var existingMember = await _memberRepository.GetMemberById(memberEvent.EntityId); + existingMember!.Apply([memberEvent]); + } +} \ No newline at end of file diff --git a/Application/Handlers/Events/PlayOfferEventHandler.cs b/Application/Handlers/Events/PlayOfferEventHandler.cs new file mode 100644 index 0000000..0ff7ff7 --- /dev/null +++ b/Application/Handlers/Events/PlayOfferEventHandler.cs @@ -0,0 +1,84 @@ +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class PlayOfferEventHandler : IRequestHandler +{ + private readonly PlayOfferRepository _playOfferRepository; + private readonly ReadEventRepository _eventRepository; + + public PlayOfferEventHandler(PlayOfferRepository playOfferRepository, ReadEventRepository eventRepository) + { + _playOfferRepository = playOfferRepository; + _eventRepository = eventRepository; + } + + public async Task Handle(TechnicalPlayOfferEvent playOfferEvent, CancellationToken cancellationToken) + { + Console.WriteLine("PlayOfferEventHandler received event: " + playOfferEvent.EventType); + var existingEvent = await _eventRepository.GetEventById(playOfferEvent.EventId); + if (existingEvent != null) + { + Console.WriteLine("Event already applied, skipping"); + return; + } + + switch (playOfferEvent.EventType) + { + case EventType.PLAYOFFER_CREATED: + await HandlePlayOfferCreatedEvent(playOfferEvent); + break; + case EventType.PLAYOFFER_CANCELLED: + await HandlePlayOfferCancelledEvent(playOfferEvent); + break; + case EventType.PLAYOFFER_JOINED: + await HandlePlayOfferJoinedEvent(playOfferEvent); + break; + case EventType.PLAYOFFER_OPPONENT_REMOVED: + await HandlePlayOfferOpponentRemovedEvent(playOfferEvent); + break; + case EventType.PLAYOFFER_RESERVATION_ADDED: + await HandlePlayOfferReservationAddedEvent(playOfferEvent); + break; + } + + await _playOfferRepository.Update(); + await _eventRepository.AppendEvent(playOfferEvent); + await _eventRepository.Update(); + } + + private async Task HandlePlayOfferReservationAddedEvent(TechnicalPlayOfferEvent playOfferEvent) + { + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(playOfferEvent.EntityId)).First(); + existingPlayOffer.Apply([playOfferEvent]); + } + + private async Task HandlePlayOfferOpponentRemovedEvent(TechnicalPlayOfferEvent playOfferEvent) + { + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(playOfferEvent.EntityId)).First(); + existingPlayOffer.Apply([playOfferEvent]); + } + + private async Task HandlePlayOfferJoinedEvent(TechnicalPlayOfferEvent playOfferEvent) + { + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(playOfferEvent.EntityId)).First(); + existingPlayOffer.Apply([playOfferEvent]); + } + + private async Task HandlePlayOfferCancelledEvent(TechnicalPlayOfferEvent playOfferEvent) + { + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(playOfferEvent.EntityId)).First(); + existingPlayOffer.Apply([playOfferEvent]); + } + + private async Task HandlePlayOfferCreatedEvent(TechnicalPlayOfferEvent playOfferEvent) + { + var newPlayOffer = new PlayOffer(); + newPlayOffer.Apply([playOfferEvent]); + _playOfferRepository.CreatePlayOffer(newPlayOffer); + } +} \ No newline at end of file diff --git a/Application/Handlers/Events/ReservationEventHandler.cs b/Application/Handlers/Events/ReservationEventHandler.cs new file mode 100644 index 0000000..09ce459 --- /dev/null +++ b/Application/Handlers/Events/ReservationEventHandler.cs @@ -0,0 +1,151 @@ +using MediatR; +using PlayOfferService.Domain; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Events.Reservation; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class ReservationEventHandler : IRequestHandler +{ + private readonly WriteEventRepository _writeEventRepository; + private readonly PlayOfferRepository _playOfferRepository; + private readonly ReadEventRepository _readEventRepository; + private readonly ReservationRepository _reservationRepository; + + public ReservationEventHandler(WriteEventRepository writeEventRepository, PlayOfferRepository playOfferRepository, ReadEventRepository readEventRepository, ReservationRepository reservationRepository) + { + _writeEventRepository = writeEventRepository; + _playOfferRepository = playOfferRepository; + _readEventRepository = readEventRepository; + _reservationRepository = reservationRepository; + } + + public async Task Handle(TechnicalReservationEvent reservationEvent, CancellationToken cancellationToken) + { + Console.WriteLine("ReservationEventHandler received event: " + reservationEvent.EventType); + var existingEvent = await _readEventRepository.GetEventById(reservationEvent.EventId); + if (existingEvent != null) + { + Console.WriteLine("Event already applied, skipping"); + return; + } + + switch (reservationEvent.EventType) + { + case EventType.ReservationCreatedEvent: + await HandleReservationCreatedEvent(reservationEvent); + break; + case EventType.ReservationRejectedEvent: + await HandleReservationRejectedEvent(reservationEvent); + break; + case EventType.ReservationLimitExceeded: + await HandleReservationLimitExceededEvent(reservationEvent); + break; + case EventType.ReservationCancelledEvent: + await HandleReservationCancelledEvent(reservationEvent); + break; + } + + await _reservationRepository.Update(); + await _readEventRepository.AppendEvent(reservationEvent); + await _readEventRepository.Update(); + } + + private async Task HandleReservationCancelledEvent(TechnicalReservationEvent reservationEvent) + { + var existingPlayOffer = await _playOfferRepository.GetPlayOfferByReservationId(reservationEvent.EntityId); + if (existingPlayOffer != null) + { + var playOfferEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EntityId = existingPlayOffer.Id, + EventType = EventType.PLAYOFFER_CANCELLED, + EntityType = EntityType.PLAYOFFER, + Timestamp = DateTime.UtcNow, + CorrelationId = reservationEvent.EventId, + EventData = new PlayOfferCancelledEvent() + }; + + await _writeEventRepository.AppendEvent(playOfferEvent); + await _writeEventRepository.Update(); + } + + var existingReservation = await _reservationRepository.GetReservationById(reservationEvent.EntityId); + existingReservation!.Apply([reservationEvent]); + } + + private async Task HandleReservationLimitExceededEvent(TechnicalReservationEvent reservationEvent) + { + var existingPlayOffer = await _playOfferRepository.GetPlayOfferByEventId((Guid)reservationEvent.CorrelationId!); + if (existingPlayOffer == null) + return; + + var playOfferEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EntityId = existingPlayOffer.Id, + EventType = EventType.PLAYOFFER_OPPONENT_REMOVED, + EntityType = EntityType.PLAYOFFER, + Timestamp = DateTime.UtcNow, + CorrelationId = reservationEvent.EventId, + EventData = new PlayOfferOpponentRemovedEvent() + }; + + await _writeEventRepository.AppendEvent(playOfferEvent); + await _writeEventRepository.Update(); + } + + private async Task HandleReservationRejectedEvent(TechnicalReservationEvent reservationEvent) + { + var existingPlayOffer = await _playOfferRepository.GetPlayOfferByEventId((Guid)reservationEvent.CorrelationId!); + if (existingPlayOffer == null) + return; + + var playOfferEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EntityId = existingPlayOffer.Id, + EventType = EventType.PLAYOFFER_OPPONENT_REMOVED, + EntityType = EntityType.PLAYOFFER, + Timestamp = DateTime.UtcNow, + CorrelationId = reservationEvent.EventId, + EventData = new PlayOfferOpponentRemovedEvent() + }; + + await _writeEventRepository.AppendEvent(playOfferEvent); + await _writeEventRepository.Update(); + } + + private async Task HandleReservationCreatedEvent(TechnicalReservationEvent reservationEvent) + { + // Check if ReservationCreatedEvent is a response to a PlayOfferJoinedEvent + if (reservationEvent.CorrelationId != null) + { + var existingPlayOffer = await _playOfferRepository.GetPlayOfferByEventId((Guid)reservationEvent.CorrelationId!); + if (existingPlayOffer == null) + return; + + var playOfferEvent = new BaseEvent + { + EventId = Guid.NewGuid(), + EntityId = existingPlayOffer.Id, + EventType = EventType.PLAYOFFER_RESERVATION_ADDED, + EntityType = EntityType.PLAYOFFER, + Timestamp = DateTime.UtcNow, + CorrelationId = reservationEvent.EventId, + EventData = new PlayOfferReservationAddedEvent{ReservationId = reservationEvent.EntityId} + }; + + await _writeEventRepository.AppendEvent(playOfferEvent); + await _writeEventRepository.Update(); + } + + var reservation = new Reservation(); + reservation.Apply([reservationEvent]); + _reservationRepository.CreateReservation(reservation); + } +} \ No newline at end of file diff --git a/Application/Handlers/GetPlayOffersByClubIdHandler.cs b/Application/Handlers/GetPlayOffersByClubIdHandler.cs new file mode 100644 index 0000000..3e78837 --- /dev/null +++ b/Application/Handlers/GetPlayOffersByClubIdHandler.cs @@ -0,0 +1,48 @@ +using MediatR; +using PlayOfferService.Application.Queries; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers; +public class GetPlayOffersByClubIdHandler : IRequestHandler> +{ + private readonly PlayOfferRepository _playOfferRepository; + private readonly MemberRepository _memberRepository; + private readonly ClubRepository _clubRepository; + private readonly ReservationRepository _reservationRepository; + private readonly CourtRepository _courtRepository; + + public GetPlayOffersByClubIdHandler(PlayOfferRepository playOfferRepository, MemberRepository memberRepository, ClubRepository clubRepository, ReservationRepository reservationRepository, CourtRepository courtRepository) + { + _playOfferRepository = playOfferRepository; + _memberRepository = memberRepository; + _clubRepository = clubRepository; + _reservationRepository = reservationRepository; + _courtRepository = courtRepository; + } + + public async Task> Handle(GetPlayOffersByClubIdQuery request, CancellationToken cancellationToken) + { + var playOffers = await _playOfferRepository.GetPlayOffersByIds(null, null, request.ClubId); + + var clubDto = new ClubDto((await _clubRepository.GetClubById(request.ClubId))!); + var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList(); + var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList(); + var reservationDtos = (await _reservationRepository.GetAllReservations()) + .Select(reservation => new ReservationDto( + reservation, + courtDtos.First(courtDto => courtDto.Id == reservation.CourtId))) + .ToList(); + + var playOfferDtos = new List(); + foreach (var playOffer in playOffers) + { + var creator = memberDtos.First(member => member.Id == playOffer.CreatorId); + var opponent = memberDtos.FirstOrDefault(member => member.Id == playOffer.OpponentId); + var reservation = reservationDtos.FirstOrDefault(reservation => reservation.Id == playOffer.ReservationId); + playOfferDtos.Add(new PlayOfferDto(playOffer, clubDto, creator, opponent, reservation)); + } + + return playOfferDtos; + } +} diff --git a/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs b/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs new file mode 100644 index 0000000..a138bdb --- /dev/null +++ b/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs @@ -0,0 +1,59 @@ +using MediatR; +using PlayOfferService.Application.Queries; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers.Events; + +public class GetPlayOffersByCreatorNameHandler : IRequestHandler> +{ + private readonly PlayOfferRepository _playOfferRepository; + private readonly MemberRepository _memberRepository; + private readonly ClubRepository _clubRepository; + private readonly ReservationRepository _reservationRepository; + private readonly CourtRepository _courtRepository; + + public GetPlayOffersByCreatorNameHandler(PlayOfferRepository playOfferRepository, MemberRepository memberRepository, ClubRepository clubRepository, ReservationRepository reservationRepository, CourtRepository courtRepository) + { + _playOfferRepository = playOfferRepository; + _memberRepository = memberRepository; + _clubRepository = clubRepository; + _reservationRepository = reservationRepository; + _courtRepository = courtRepository; + } + + public async Task> Handle(GetPlayOffersByCreatorNameQuery request, CancellationToken cancellationToken) + { + if (request.CreatorName.Split(" ").Length > 2) + throw new ArgumentException("Creator name must be in the format ' ', '' or ''"); + + var creators = await _memberRepository.GetMemberByName(request.CreatorName); + var playOffers = new List(); + foreach (var creator in creators) + { + var playOffersByCreator = await _playOfferRepository.GetPlayOffersByIds(null, creator.Id); + playOffers.AddRange(playOffersByCreator); + } + + var clubDto = (await _clubRepository.GetAllClubs()).Select(club => new ClubDto(club)).ToList(); + var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList(); + var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList(); + var reservationDtos = (await _reservationRepository.GetAllReservations()) + .Select(reservation => new ReservationDto( + reservation, + courtDtos.First(courtDto => courtDto.Id == reservation.CourtId))) + .ToList(); + + var playOfferDtos = new List(); + foreach (var playOffer in playOffers) + { + var club = clubDto.First(club => club.Id == playOffer.ClubId); + var creator = memberDtos.First(member => member.Id == playOffer.CreatorId); + var opponent = memberDtos.FirstOrDefault(member => member.Id == playOffer.OpponentId); + var reservation = reservationDtos.FirstOrDefault(reservation => reservation.Id == playOffer.ReservationId); + playOfferDtos.Add(new PlayOfferDto(playOffer, club, creator, opponent, reservation)); + } + + return playOfferDtos; + } +} \ No newline at end of file diff --git a/Application/Handlers/GetPlayOffersByIdHandler.cs b/Application/Handlers/GetPlayOffersByIdHandler.cs deleted file mode 100644 index ec83b02..0000000 --- a/Application/Handlers/GetPlayOffersByIdHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MediatR; -using PlayOfferService.Application.Queries; -using PlayOfferService.Domain.Models; -using PlayOfferService.Domain.Repositories; - -namespace PlayOfferService.Application.Handlers; -public class GetPlayOffersByIdHandler : IRequestHandler> -{ - private readonly PlayOfferRepository _playOfferRepository; - - public GetPlayOffersByIdHandler(PlayOfferRepository playOfferRepository) - { - _playOfferRepository = playOfferRepository; - } - - public async Task> Handle(GetPlayOffersByIdQuery request, CancellationToken cancellationToken) - { - return await _playOfferRepository.GetPlayOffersByIds(request.PlayOfferId, request.CreatorId, request.ClubId); - } -} diff --git a/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs b/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs new file mode 100644 index 0000000..bf92560 --- /dev/null +++ b/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs @@ -0,0 +1,49 @@ +using MediatR; +using PlayOfferService.Application.Queries; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.Repositories; + +namespace PlayOfferService.Application.Handlers; +public class GetPlayOffersByParticipantIdHandler : IRequestHandler> +{ + private readonly PlayOfferRepository _playOfferRepository; + private readonly MemberRepository _memberRepository; + private readonly ClubRepository _clubRepository; + private readonly ReservationRepository _reservationRepository; + private readonly CourtRepository _courtRepository; + + public GetPlayOffersByParticipantIdHandler(PlayOfferRepository playOfferRepository, MemberRepository memberRepository, ClubRepository clubRepository, ReservationRepository reservationRepository, CourtRepository courtRepository) + { + _playOfferRepository = playOfferRepository; + _memberRepository = memberRepository; + _clubRepository = clubRepository; + _reservationRepository = reservationRepository; + _courtRepository = courtRepository; + } + + public async Task> Handle(GetPlayOffersByParticipantIdQuery request, CancellationToken cancellationToken) + { + var playOffers = await _playOfferRepository.GetPlayOffersByParticipantId(request.ParticipantId); + + var clubDto = (await _clubRepository.GetAllClubs()).Select(club => new ClubDto(club)).ToList(); + var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList(); + var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList(); + var reservationDtos = (await _reservationRepository.GetAllReservations()) + .Select(reservation => new ReservationDto( + reservation, + courtDtos.First(courtDto => courtDto.Id == reservation.CourtId))) + .ToList(); + + var playOfferDtos = new List(); + foreach (var playOffer in playOffers) + { + var club = clubDto.First(club => club.Id == playOffer.ClubId); + var creator = memberDtos.First(member => member.Id == playOffer.CreatorId); + var opponent = memberDtos.FirstOrDefault(member => member.Id == playOffer.OpponentId); + var reservation = reservationDtos.FirstOrDefault(reservation => reservation.Id == playOffer.ReservationId); + playOfferDtos.Add(new PlayOfferDto(playOffer, club, creator, opponent, reservation)); + } + + return playOfferDtos; + } +} diff --git a/Application/Handlers/JoinPlayOfferHandler.cs b/Application/Handlers/JoinPlayOfferHandler.cs index ccd64ae..c3bb762 100644 --- a/Application/Handlers/JoinPlayOfferHandler.cs +++ b/Application/Handlers/JoinPlayOfferHandler.cs @@ -11,14 +11,14 @@ namespace PlayOfferService.Application.Handlers; public class JoinPlayOfferHandler : IRequestHandler { - private readonly DbWriteContext _context; + private readonly WriteEventRepository _writeEventRepository; private readonly PlayOfferRepository _playOfferRepository; private readonly MemberRepository _memberRepository; private readonly ClubRepository _clubRepository; - public JoinPlayOfferHandler(DbWriteContext context, PlayOfferRepository playOfferRepository, MemberRepository memberRepository, ClubRepository clubRepository) + public JoinPlayOfferHandler(WriteEventRepository writeEventRepository, PlayOfferRepository playOfferRepository, MemberRepository memberRepository, ClubRepository clubRepository) { - _context = context; + _writeEventRepository = writeEventRepository; _playOfferRepository = playOfferRepository; _memberRepository = memberRepository; _clubRepository = clubRepository; @@ -26,13 +26,16 @@ public JoinPlayOfferHandler(DbWriteContext context, PlayOfferRepository playOffe public async Task Handle(JoinPlayOfferCommand request, CancellationToken cancellationToken) { + var transaction = _writeEventRepository.StartTransaction(); + var excpectedEventCount = _writeEventRepository.GetEventCount(request.JoinPlayOfferDto.PlayOfferId) + 1; + var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(request.JoinPlayOfferDto.PlayOfferId)).FirstOrDefault(); if (existingPlayOffer == null) throw new NotFoundException($"PlayOffer {request.JoinPlayOfferDto.PlayOfferId} not found!"); - var existingOpponent = await _memberRepository.GetMemberById(request.JoinPlayOfferDto.OpponentId); + var existingOpponent = await _memberRepository.GetMemberById(request.MemberId); if (existingOpponent == null) - throw new NotFoundException($"Member {request.JoinPlayOfferDto.OpponentId} not found!"); + throw new NotFoundException($"Member {request.MemberId} not found!"); if (existingOpponent.Id == existingPlayOffer.CreatorId) throw new InvalidOperationException("Can't join your own PlayOffer!"); @@ -79,8 +82,16 @@ public async Task Handle(JoinPlayOfferCommand request, CancellationToken c Timestamp = DateTime.UtcNow }; - _context.Events.Add(domainEvent); - await _context.SaveChangesAsync(); + await _writeEventRepository.AppendEvent(domainEvent); + await _writeEventRepository.Update(); + + var eventCount = _writeEventRepository.GetEventCount(request.JoinPlayOfferDto.PlayOfferId); + + if (eventCount != excpectedEventCount) + { + transaction.Rollback(); + throw new InvalidOperationException("Concurrent modification detected!"); + } return Task.CompletedTask; } diff --git a/Application/Queries/GetPlayOffersByClubIdQuery.cs b/Application/Queries/GetPlayOffersByClubIdQuery.cs new file mode 100644 index 0000000..a58df97 --- /dev/null +++ b/Application/Queries/GetPlayOffersByClubIdQuery.cs @@ -0,0 +1,7 @@ +using MediatR; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Application.Queries; +public record GetPlayOffersByClubIdQuery(Guid ClubId) : IRequest> +{ +} diff --git a/Application/Queries/GetPlayOffersByCreatorNameQuery.cs b/Application/Queries/GetPlayOffersByCreatorNameQuery.cs new file mode 100644 index 0000000..d360413 --- /dev/null +++ b/Application/Queries/GetPlayOffersByCreatorNameQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Application.Queries; + +public record GetPlayOffersByCreatorNameQuery(string CreatorName) : IRequest> +{ +} \ No newline at end of file diff --git a/Application/Queries/GetPlayOffersByIdQuery.cs b/Application/Queries/GetPlayOffersByIdQuery.cs deleted file mode 100644 index 1d6aa13..0000000 --- a/Application/Queries/GetPlayOffersByIdQuery.cs +++ /dev/null @@ -1,7 +0,0 @@ -using MediatR; -using PlayOfferService.Domain.Models; - -namespace PlayOfferService.Application.Queries; -public record GetPlayOffersByIdQuery(Guid? PlayOfferId, Guid? CreatorId, Guid? ClubId) : IRequest> -{ -} diff --git a/Application/Queries/GetPlayOffersByParticipantIdQuery.cs b/Application/Queries/GetPlayOffersByParticipantIdQuery.cs new file mode 100644 index 0000000..945f74c --- /dev/null +++ b/Application/Queries/GetPlayOffersByParticipantIdQuery.cs @@ -0,0 +1,8 @@ +using MediatR; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Application.Queries; + +public record GetPlayOffersByParticipantIdQuery(Guid ParticipantId) : IRequest> +{ +} \ No newline at end of file diff --git a/Application/RedisClubStreamService.cs b/Application/RedisClubStreamService.cs index 0fb9cdd..c7f8317 100644 --- a/Application/RedisClubStreamService.cs +++ b/Application/RedisClubStreamService.cs @@ -1,7 +1,7 @@ -using System.Text.Json.Nodes; +using MediatR; using PlayOfferService.Domain.Events; -using PlayOfferService.Domain.Repositories; using StackExchange.Redis; +using System.Text.Json.Nodes; namespace PlayOfferService.Application; @@ -12,28 +12,28 @@ public class RedisClubStreamService : BackgroundService private readonly IDatabase _db; private const string StreamName = "club_service_events.public.DomainEvent"; private const string GroupName = "pos.club.events.group"; - - + + public RedisClubStreamService(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; var tokenSource = new CancellationTokenSource(); _cancellationToken = tokenSource.Token; - var muxer = ConnectionMultiplexer.Connect("pos_redis"); + var muxer = ConnectionMultiplexer.Connect("redis"); _db = muxer.GetDatabase(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using IServiceScope scope = _serviceScopeFactory.CreateScope(); - ClubRepository clubRepository = scope.ServiceProvider.GetRequiredService(); - + IMediator mediator = scope.ServiceProvider.GetRequiredService(); + if (!(await _db.KeyExistsAsync(StreamName)) || - (await _db.StreamGroupInfoAsync(StreamName)).All(x=>x.Name!=GroupName)) + (await _db.StreamGroupInfoAsync(StreamName)).All(x => x.Name != GroupName)) { await _db.StreamCreateConsumerGroupAsync(StreamName, GroupName, "0-0"); } - + var id = string.Empty; while (!_cancellationToken.IsCancellationRequested) @@ -51,30 +51,32 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var parsedEvent = FilterandParseEvent(streamEntry); if (parsedEvent == null) continue; - await clubRepository.UpdateEntityAsync(parsedEvent); + await mediator.Send(parsedEvent, _cancellationToken); } await Task.Delay(1000); } } - - private BaseEvent? FilterandParseEvent(StreamEntry value) + + private TechnicalClubEvent? FilterandParseEvent(StreamEntry value) { var dict = value.Values.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()); var jsonContent = JsonNode.Parse(dict.Values.First()); var eventInfo = jsonContent["payload"]["after"]; - + var eventType = eventInfo["eventType"].GetValue(); var entityType = eventInfo["entityType"].GetValue(); - + if ((eventType != "TENNIS_CLUB_REGISTERED" && eventType != "TENNIS_CLUB_LOCKED" && eventType != "TENNIS_CLUB_UNLOCKED" - && eventType != "TENNIS_CLUB_DELETED") || entityType != "TENNIS_CLUB") + && eventType != "TENNIS_CLUB_DELETED" + && eventType != "TENNIS_CLUB_NAME_CHANGED" + ) || entityType != "TENNIS_CLUB") { return null; } - - return EventParser.ParseEvent(eventInfo); + + return EventParser.ParseEvent(eventInfo); } } \ No newline at end of file diff --git a/Application/RedisCourtStreamService.cs b/Application/RedisCourtStreamService.cs new file mode 100644 index 0000000..30c417f --- /dev/null +++ b/Application/RedisCourtStreamService.cs @@ -0,0 +1,83 @@ +using System.Text.Json.Nodes; +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Court; +using StackExchange.Redis; + +namespace PlayOfferService.Application; + +public class RedisCourtStreamService : BackgroundService +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly CancellationToken _cancellationToken; + private readonly IDatabase _db; + private const string StreamName = "court_service.events.baseevents"; + private const string GroupName = "pos.courts.events.group"; + + + public RedisCourtStreamService(IServiceScopeFactory serviceScopeFactory) + { + _serviceScopeFactory = serviceScopeFactory; + var tokenSource = new CancellationTokenSource(); + _cancellationToken = tokenSource.Token; + var muxer = ConnectionMultiplexer.Connect("redis"); + _db = muxer.GetDatabase(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IMediator mediator = scope.ServiceProvider.GetRequiredService(); + + if (!(await _db.KeyExistsAsync(StreamName)) || + (await _db.StreamGroupInfoAsync(StreamName)).All(x=>x.Name!=GroupName)) + { + await _db.StreamCreateConsumerGroupAsync(StreamName, GroupName, "0-0"); + } + + + var id = string.Empty; + while (!_cancellationToken.IsCancellationRequested) + { + if (!string.IsNullOrEmpty(id)) + { + await _db.StreamAcknowledgeAsync(StreamName, GroupName, id); + id = string.Empty; + } + var result = await _db.StreamReadGroupAsync(StreamName, GroupName, "pos-club", ">", 1); + if (result.Any()) + { + var streamEntry = result.First(); + id = streamEntry.Id; + var parsedEvent = FilterandParseEvent(streamEntry); + if (parsedEvent == null) + continue; + await mediator.Send(parsedEvent, _cancellationToken); + } + await Task.Delay(1000); + } + + } + + private TechnicalCourtEvent? FilterandParseEvent(StreamEntry value) + { + var dict = value.Values.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()); + var jsonContent = JsonNode.Parse(dict.Values.First()); + var eventInfo = JsonNode.Parse(jsonContent["payload"]["after"].GetValue()); + + var eventType = eventInfo["eventType"].GetValue(); + var entityType = eventInfo["entityType"].GetValue(); + var acceptedEventTypes = new List + { + "CourtCreatedEvent", + "CourtUpdatedEvent" + }; + + if (!acceptedEventTypes.Contains(eventType) || entityType != "Court") + { + return null; + } + + return EventParser.ParseEvent(eventInfo); + } +} \ No newline at end of file diff --git a/Application/RedisMemberStreamService.cs b/Application/RedisMemberStreamService.cs index 8be7ae2..9c76996 100644 --- a/Application/RedisMemberStreamService.cs +++ b/Application/RedisMemberStreamService.cs @@ -1,5 +1,7 @@ using System.Text.Json.Nodes; +using MediatR; using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Member; using PlayOfferService.Domain.Repositories; using StackExchange.Redis; @@ -19,14 +21,14 @@ public RedisMemberStreamService(IServiceScopeFactory serviceScopeFactory) _serviceScopeFactory = serviceScopeFactory; var tokenSource = new CancellationTokenSource(); _cancellationToken = tokenSource.Token; - var muxer = ConnectionMultiplexer.Connect("pos_redis"); + var muxer = ConnectionMultiplexer.Connect("redis"); _db = muxer.GetDatabase(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using IServiceScope scope = _serviceScopeFactory.CreateScope(); - MemberRepository memberRepository = scope.ServiceProvider.GetRequiredService(); + IMediator mediator = scope.ServiceProvider.GetRequiredService(); if (!(await _db.KeyExistsAsync(StreamName)) || (await _db.StreamGroupInfoAsync(StreamName)).All(x=>x.Name!=GroupName)) @@ -50,13 +52,13 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var parsedEvent = ParseEvent(streamEntry); if (parsedEvent == null) continue; - await memberRepository.UpdateEntityAsync(parsedEvent); + await mediator.Send(parsedEvent, _cancellationToken); } await Task.Delay(1000); } } - private BaseEvent? ParseEvent(StreamEntry value) + private TechnicalMemberEvent? ParseEvent(StreamEntry value) { var dict = value.Values.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()); var jsonContent = JsonNode.Parse(dict.Values.First()); @@ -65,14 +67,20 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var eventType = eventInfo["eventType"].GetValue(); var entityType = eventInfo["entityType"].GetValue(); - if ((eventType != "MEMBER_REGISTERED" + if ( + (eventType != "MEMBER_REGISTERED" && eventType != "MEMBER_DELETED" && eventType != "MEMBER_LOCKED" - && eventType != "MEMBER_UNLOCKED") || entityType != "MEMBER") + && eventType != "MEMBER_UNLOCKED" + && eventType != "MEMBER_EMAIL_CHANGED" + && eventType != "MEMBER_FULL_NAME_CHANGED" + ) + || entityType != "MEMBER" + ) { return null; } - return EventParser.ParseEvent(eventInfo); + return EventParser.ParseEvent(eventInfo); } } \ No newline at end of file diff --git a/Application/RedisPlayOfferStreamService.cs b/Application/RedisPlayOfferStreamService.cs index 6a86ad9..f1b6d72 100644 --- a/Application/RedisPlayOfferStreamService.cs +++ b/Application/RedisPlayOfferStreamService.cs @@ -1,5 +1,7 @@ using System.Text.Json.Nodes; +using MediatR; using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; using PlayOfferService.Domain.Repositories; using StackExchange.Redis; @@ -19,14 +21,14 @@ public RedisPlayOfferStreamService(IServiceScopeFactory serviceScopeFactory) _serviceScopeFactory = serviceScopeFactory; var tokenSource = new CancellationTokenSource(); _cancellationToken = tokenSource.Token; - var muxer = ConnectionMultiplexer.Connect("pos_redis"); + var muxer = ConnectionMultiplexer.Connect("redis"); _db = muxer.GetDatabase(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using IServiceScope scope = _serviceScopeFactory.CreateScope(); - PlayOfferRepository playOfferRepository = scope.ServiceProvider.GetRequiredService(); + IMediator mediator = scope.ServiceProvider.GetRequiredService(); if (!(await _db.KeyExistsAsync(StreamName)) || (await _db.StreamGroupInfoAsync(StreamName)).All(x=>x.Name!=GroupName)) @@ -49,18 +51,18 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) var streamEntry = result.First(); id = streamEntry.Id; var parsedEvent = ParseEvent(streamEntry); - await playOfferRepository.UpdateEntityAsync(parsedEvent); + await mediator.Send(parsedEvent, _cancellationToken); } await Task.Delay(1000); } } - private BaseEvent ParseEvent(StreamEntry value) + private TechnicalPlayOfferEvent ParseEvent(StreamEntry value) { var dict = value.Values.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()); var jsonContent = JsonNode.Parse(dict.Values.First()); var eventInfo = jsonContent["payload"]["after"]; - return EventParser.ParseEvent(eventInfo); + return EventParser.ParseEvent(eventInfo); } } \ No newline at end of file diff --git a/Application/RedisReservationStreamService.cs b/Application/RedisReservationStreamService.cs new file mode 100644 index 0000000..26655e0 --- /dev/null +++ b/Application/RedisReservationStreamService.cs @@ -0,0 +1,76 @@ +using System.Text.Json; +using System.Text.Json.Nodes; +using MediatR; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Reservation; +using PlayOfferService.Domain.Repositories; +using StackExchange.Redis; + +namespace PlayOfferService.Application; + +public class RedisReservationStreamService : BackgroundService +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly CancellationToken _cancellationToken; + private readonly IDatabase _db; + private const string StreamName = "court_service.events.baseevents"; + private const string GroupName = "pos.reservation.events.group"; + + + public RedisReservationStreamService(IServiceScopeFactory serviceScopeFactory) + { + _serviceScopeFactory = serviceScopeFactory; + var tokenSource = new CancellationTokenSource(); + _cancellationToken = tokenSource.Token; + var muxer = ConnectionMultiplexer.Connect("redis"); + _db = muxer.GetDatabase(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IMediator mediator = scope.ServiceProvider.GetRequiredService(); + + if (!(await _db.KeyExistsAsync(StreamName)) || + (await _db.StreamGroupInfoAsync(StreamName)).All(x=>x.Name!=GroupName)) + { + await _db.StreamCreateConsumerGroupAsync(StreamName, GroupName, "0-0", true); + } + + var id = string.Empty; + while (!_cancellationToken.IsCancellationRequested) + { + if (!string.IsNullOrEmpty(id)) + { + await _db.StreamAcknowledgeAsync(StreamName, GroupName, id); + id = string.Empty; + } + var result = await _db.StreamReadGroupAsync(StreamName, GroupName, "pos-member", ">", 1); + if (result.Any()) + { + var streamEntry = result.First(); + id = streamEntry.Id; + var parsedEvent = FilterAndParseEvent(streamEntry); + if (parsedEvent == null) + continue; + + await mediator.Send(parsedEvent, _cancellationToken); + } + await Task.Delay(1000); + } + } + + private TechnicalReservationEvent? FilterAndParseEvent(StreamEntry value) + { + var dict = value.Values.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()); + var jsonContent = JsonNode.Parse(dict.Values.First()); + var eventInfo = JsonNode.Parse(jsonContent["payload"]["after"].GetValue()); + + var entityType = eventInfo["entityType"].GetValue(); + if (entityType != "Reservation") + return null; + + + return EventParser.ParseEvent(eventInfo); + } +} \ No newline at end of file diff --git a/Domain/DbReadContext.cs b/Domain/DbReadContext.cs index 8ce2230..ebd3508 100644 --- a/Domain/DbReadContext.cs +++ b/Domain/DbReadContext.cs @@ -10,39 +10,39 @@ public class DbReadContext : DbContext public DbReadContext(DbContextOptions options) : base(options) { } - + public DbSet PlayOffers { get; set; } public DbSet Clubs { get; set; } public DbSet Members { get; set; } public DbSet Reservations { get; set; } - + public DbSet Courts { get; set; } public DbSet AppliedEvents { get; set; } - + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfiguration(new BaseEventConfiguration()); - + // TODO: Remove before coop testing - var testClub = new Club{Id = Guid.Parse("06b812a7-5131-4510-82ff-bffac33e0f3e"), Status = Status.ACTIVE}; - var testMemberIds = new List {Guid.Parse("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), Guid.Parse("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d")}; - + var testClub = new Club { Id = Guid.Parse("1fc64a89-9e63-4e9f-96f7-e2120f0ca6c3"), Name = "Test Club", Status = Status.ACTIVE }; + var testMemberIds = new List { Guid.Parse("0033506d-2d59-40d3-b996-74a251091027"), Guid.Parse("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d") }; + var testMembers = new List{ - new {Id = testMemberIds[0], ClubId = testClub.Id, Status = Status.ACTIVE}, - new {Id = testMemberIds[1], ClubId = testClub.Id, Status = Status.ACTIVE} + new {Id = testMemberIds[0], ClubId = testClub.Id, Status = Status.ACTIVE, FirstName = "Hans", LastName="Müller", Email="hans@müller.de"}, + new {Id = testMemberIds[1], ClubId = testClub.Id, Status = Status.ACTIVE, FirstName = "Friedrich", LastName="Bäcker", Email="friedrich@bäcker.at"} }; - + // Need to directly specify foreign keys for seeding var testPlayOffer = new { Id = Guid.NewGuid(), ClubId = testClub.Id, - CreatorId = testMemberIds[0], + CreatorId = testMemberIds[1], ProposedStartTime = DateTime.UtcNow, ProposedEndTime = DateTime.UtcNow.AddHours(1), IsCancelled = false }; - + modelBuilder.Entity().HasData(testClub); foreach (var testMember in testMembers) { diff --git a/Domain/Events/Club/ClubNameChangedEvent.cs b/Domain/Events/Club/ClubNameChangedEvent.cs new file mode 100644 index 0000000..4f908a3 --- /dev/null +++ b/Domain/Events/Club/ClubNameChangedEvent.cs @@ -0,0 +1,6 @@ +namespace PlayOfferService.Domain.Events.Club; + +public class ClubNameChangedEvent : DomainEvent +{ + public string Name { get; set; } +} diff --git a/Domain/Events/Club/TechnicalClubEvent.cs b/Domain/Events/Club/TechnicalClubEvent.cs new file mode 100644 index 0000000..b6a2405 --- /dev/null +++ b/Domain/Events/Club/TechnicalClubEvent.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace PlayOfferService.Domain.Events; + +public class TechnicalClubEvent : BaseEvent, IRequest +{ +} \ No newline at end of file diff --git a/Domain/Events/Court/CourtCreatedEvent.cs b/Domain/Events/Court/CourtCreatedEvent.cs new file mode 100644 index 0000000..4bfb55d --- /dev/null +++ b/Domain/Events/Court/CourtCreatedEvent.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Domain.Events.Court; + +public class CourtCreatedEvent : DomainEvent +{ + [JsonPropertyName("clubId")] + public Guid ClubId { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("restrictions")] + public List Restrictions { get; set; } +} \ No newline at end of file diff --git a/Domain/Events/Court/CourtUpdatedEvent.cs b/Domain/Events/Court/CourtUpdatedEvent.cs new file mode 100644 index 0000000..4b3aabf --- /dev/null +++ b/Domain/Events/Court/CourtUpdatedEvent.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Domain.Events.Court; + +public class CourtUpdatedEvent : DomainEvent +{ + [JsonPropertyName("courtName")] + public string? CourtName { get; set; } + [JsonPropertyName("restrictions")] + public List? Restrictions { get; set; } +} \ No newline at end of file diff --git a/Domain/Events/Court/TechnicalCourtEvent.cs b/Domain/Events/Court/TechnicalCourtEvent.cs new file mode 100644 index 0000000..c290c97 --- /dev/null +++ b/Domain/Events/Court/TechnicalCourtEvent.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace PlayOfferService.Domain.Events.Court; + +public class TechnicalCourtEvent : BaseEvent, IRequest +{ +} \ No newline at end of file diff --git a/Domain/Events/IDomainEvent.cs b/Domain/Events/DomainEvent.cs similarity index 50% rename from Domain/Events/IDomainEvent.cs rename to Domain/Events/DomainEvent.cs index c96558e..6773c77 100644 --- a/Domain/Events/IDomainEvent.cs +++ b/Domain/Events/DomainEvent.cs @@ -1,6 +1,9 @@ -using System.Text.Json.Serialization; +using PlayOfferService.Domain.Events.Club; +using PlayOfferService.Domain.Events.Court; using PlayOfferService.Domain.Events.Member; using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Events.Reservation; +using System.Text.Json.Serialization; namespace PlayOfferService.Domain.Events; @@ -9,14 +12,24 @@ namespace PlayOfferService.Domain.Events; [JsonDerivedType(typeof(ClubLockedEvent), typeDiscriminator: "TENNIS_CLUB_LOCKED")] [JsonDerivedType(typeof(ClubUnlockedEvent), typeDiscriminator: "TENNIS_CLUB_UNLOCKED")] [JsonDerivedType(typeof(ClubDeletedEvent), typeDiscriminator: "TENNIS_CLUB_DELETED")] +[JsonDerivedType(typeof(ClubNameChangedEvent), typeDiscriminator: "TENNIS_CLUB_NAME_CHANGED")] [JsonDerivedType(typeof(MemberCreatedEvent), typeDiscriminator: "MEMBER_REGISTERED")] [JsonDerivedType(typeof(MemberLockedEvent), typeDiscriminator: "MEMBER_LOCKED")] [JsonDerivedType(typeof(MemberUnlockedEvent), typeDiscriminator: "MEMBER_UNLOCKED")] [JsonDerivedType(typeof(MemberDeletedEvent), typeDiscriminator: "MEMBER_DELETED")] +[JsonDerivedType(typeof(MemberEmailChangedEvent), typeDiscriminator: "MEMBER_EMAIL_CHANGED")] +[JsonDerivedType(typeof(MemberFullNameChangedEvent), typeDiscriminator: "MEMBER_FULL_NAME_CHANGED")] [JsonDerivedType(typeof(PlayOfferCreatedEvent), typeDiscriminator: "PLAYOFFER_CREATED")] [JsonDerivedType(typeof(PlayOfferJoinedEvent), typeDiscriminator: "PLAYOFFER_JOINED")] [JsonDerivedType(typeof(PlayOfferCancelledEvent), typeDiscriminator: "PLAYOFFER_CANCELLED")] -[JsonDerivedType(typeof(PlayOfferReservationCreatedEvent), typeDiscriminator: "PLAYOFFER_RESERVATION_CREATED")] +[JsonDerivedType(typeof(PlayOfferOpponentRemovedEvent), typeDiscriminator: "PLAYOFFER_OPPONENT_REMOVED")] +[JsonDerivedType(typeof(PlayOfferReservationAddedEvent), typeDiscriminator: "PLAYOFFER_RESERVATION_ADDED")] +[JsonDerivedType(typeof(ReservationCreatedEvent), typeDiscriminator: "ReservationCreatedEvent")] +[JsonDerivedType(typeof(ReservationRejectedEvent), typeDiscriminator: "ReservationRejectedEvent")] +[JsonDerivedType(typeof(ReservationLimitExceededEvent), typeDiscriminator: "ReservationLimitExceededEvent")] +[JsonDerivedType(typeof(ReservationCancelledEvent), typeDiscriminator: "ReservationCanceledEvent")] +[JsonDerivedType(typeof(CourtCreatedEvent), typeDiscriminator: "CourtCreatedEvent")] +[JsonDerivedType(typeof(CourtUpdatedEvent), typeDiscriminator: "CourtUpdatedEvent")] public class DomainEvent { } \ No newline at end of file diff --git a/Domain/Events/EntityType.cs b/Domain/Events/EntityType.cs index 85c0c49..cf229b0 100644 --- a/Domain/Events/EntityType.cs +++ b/Domain/Events/EntityType.cs @@ -3,7 +3,8 @@ namespace PlayOfferService.Domain.Events; public enum EntityType { PLAYOFFER, - RESERVATION, + Reservation, + Court, MEMBER, TENNIS_CLUB } \ No newline at end of file diff --git a/Domain/Events/EventType.cs b/Domain/Events/EventType.cs index 256aa05..d56fefd 100644 --- a/Domain/Events/EventType.cs +++ b/Domain/Events/EventType.cs @@ -5,13 +5,23 @@ public enum EventType PLAYOFFER_CREATED, PLAYOFFER_JOINED, PLAYOFFER_CANCELLED, - PLAYOFFER_RESERVATION_CREATED, + PLAYOFFER_OPPONENT_REMOVED, + PLAYOFFER_RESERVATION_ADDED, TENNIS_CLUB_REGISTERED, TENNIS_CLUB_LOCKED, TENNIS_CLUB_UNLOCKED, TENNIS_CLUB_DELETED, + TENNIS_CLUB_NAME_CHANGED, MEMBER_REGISTERED, MEMBER_LOCKED, MEMBER_UNLOCKED, MEMBER_DELETED, + MEMBER_EMAIL_CHANGED, + MEMBER_FULL_NAME_CHANGED, + ReservationCreatedEvent, + ReservationRejectedEvent, + ReservationLimitExceeded, + ReservationCancelledEvent, + CourtCreatedEvent, + CourtUpdatedEvent, } \ No newline at end of file diff --git a/Domain/Events/Member/MemberEmailChangedEvent.cs b/Domain/Events/Member/MemberEmailChangedEvent.cs new file mode 100644 index 0000000..bbe8ac3 --- /dev/null +++ b/Domain/Events/Member/MemberEmailChangedEvent.cs @@ -0,0 +1,6 @@ +namespace PlayOfferService.Domain.Events.Member; + +public class MemberEmailChangedEvent : DomainEvent +{ + public string Email { get; set; } +} \ No newline at end of file diff --git a/Domain/Events/Member/MemberFullNameChangedEvent.cs b/Domain/Events/Member/MemberFullNameChangedEvent.cs new file mode 100644 index 0000000..4bb30e4 --- /dev/null +++ b/Domain/Events/Member/MemberFullNameChangedEvent.cs @@ -0,0 +1,8 @@ +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Domain.Events.Member; + +public class MemberFullNameChangedEvent : DomainEvent +{ + public FullName FullName { get; set; } +} \ No newline at end of file diff --git a/Domain/Events/Member/TechnicalMemberEvent.cs b/Domain/Events/Member/TechnicalMemberEvent.cs new file mode 100644 index 0000000..807ef85 --- /dev/null +++ b/Domain/Events/Member/TechnicalMemberEvent.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace PlayOfferService.Domain.Events.Member; + +public class TechnicalMemberEvent: BaseEvent, IRequest +{ +} \ No newline at end of file diff --git a/Domain/Events/PlayOffer/PlayOfferOpponentRemovedEvent.cs b/Domain/Events/PlayOffer/PlayOfferOpponentRemovedEvent.cs new file mode 100644 index 0000000..c85da5e --- /dev/null +++ b/Domain/Events/PlayOffer/PlayOfferOpponentRemovedEvent.cs @@ -0,0 +1,5 @@ +namespace PlayOfferService.Domain.Events.PlayOffer; + +public class PlayOfferOpponentRemovedEvent: DomainEvent +{ +} \ No newline at end of file diff --git a/Domain/Events/PlayOffer/PlayOfferReservationAddedEvent.cs b/Domain/Events/PlayOffer/PlayOfferReservationAddedEvent.cs new file mode 100644 index 0000000..77378d7 --- /dev/null +++ b/Domain/Events/PlayOffer/PlayOfferReservationAddedEvent.cs @@ -0,0 +1,6 @@ +namespace PlayOfferService.Domain.Events.PlayOffer; + +public class PlayOfferReservationAddedEvent : DomainEvent +{ + public Guid ReservationId { get; set; } +} \ No newline at end of file diff --git a/Domain/Events/PlayOffer/PlayOfferReservationCreatedEvent.cs b/Domain/Events/PlayOffer/PlayOfferReservationCreatedEvent.cs deleted file mode 100644 index 02412e5..0000000 --- a/Domain/Events/PlayOffer/PlayOfferReservationCreatedEvent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using PlayOfferService.Domain.Models; - -namespace PlayOfferService.Domain.Events.PlayOffer; - -public class PlayOfferReservationCreatedEvent : DomainEvent -{ - public Reservation Reservation { get; set; } - - public PlayOfferReservationCreatedEvent(){} - - public PlayOfferReservationCreatedEvent( - Reservation reservation) - { - Reservation = reservation; - } -} - diff --git a/Domain/Events/PlayOffer/TechnicalPlayOfferEvent.cs b/Domain/Events/PlayOffer/TechnicalPlayOfferEvent.cs new file mode 100644 index 0000000..0a3aeaf --- /dev/null +++ b/Domain/Events/PlayOffer/TechnicalPlayOfferEvent.cs @@ -0,0 +1,7 @@ +using MediatR; + +namespace PlayOfferService.Domain.Events.PlayOffer; + +public class TechnicalPlayOfferEvent : BaseEvent, IRequest +{ +} \ No newline at end of file diff --git a/Domain/Events/Reservation/ReservationCancelledEvent.cs b/Domain/Events/Reservation/ReservationCancelledEvent.cs new file mode 100644 index 0000000..41c686e --- /dev/null +++ b/Domain/Events/Reservation/ReservationCancelledEvent.cs @@ -0,0 +1,5 @@ +namespace PlayOfferService.Domain.Events.Reservation; + +public class ReservationCancelledEvent : DomainEvent +{ +} \ No newline at end of file diff --git a/Domain/Events/Reservation/ReservationCreatedEvent.cs b/Domain/Events/Reservation/ReservationCreatedEvent.cs new file mode 100644 index 0000000..f82e2d2 --- /dev/null +++ b/Domain/Events/Reservation/ReservationCreatedEvent.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace PlayOfferService.Domain.Events.Reservation; + +public class ReservationCreatedEvent : DomainEvent +{ + [JsonPropertyName("start")] + public DateTime Start { get; set; } + [JsonPropertyName("end")] + public DateTime End { get; set; } + [JsonPropertyName("reservantId")] + public Guid? ReservantId { get; set; } + [JsonPropertyName("tournamentId")] + public Guid? TournamentId { get; set; } + [JsonPropertyName("participantIds")] + public List? ParticipantIds { get; set; } + [JsonPropertyName("courtIds")] + public Guid CourtId { get; set; } + + public ReservationCreatedEvent(){} +} + diff --git a/Domain/Events/Reservation/ReservationLimitExceededEvent.cs b/Domain/Events/Reservation/ReservationLimitExceededEvent.cs new file mode 100644 index 0000000..d19d13b --- /dev/null +++ b/Domain/Events/Reservation/ReservationLimitExceededEvent.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace PlayOfferService.Domain.Events.Reservation; + +public class ReservationLimitExceededEvent : DomainEvent +{ + [JsonPropertyName("start")] + public DateTime Start { get; set; } + [JsonPropertyName("end")] + public DateTime End { get; set; } + [JsonPropertyName("reservantId")] + public Guid? ReservantId { get; set; } + [JsonPropertyName("tournamentId")] + public Guid? TournamentId { get; set; } + [JsonPropertyName("participantIds")] + public List? ParticipantIds { get; set; } + [JsonPropertyName("courtIds")] + public Guid CourtId { get; set; } + + public ReservationLimitExceededEvent(){} +} \ No newline at end of file diff --git a/Domain/Events/Reservation/ReservationRejectedEvent.cs b/Domain/Events/Reservation/ReservationRejectedEvent.cs new file mode 100644 index 0000000..ad4bbb7 --- /dev/null +++ b/Domain/Events/Reservation/ReservationRejectedEvent.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; +using MediatR; + +namespace PlayOfferService.Domain.Events.Reservation; + +public class ReservationRejectedEvent : DomainEvent +{ + [JsonPropertyName("start")] + public DateTime Start { get; set; } + [JsonPropertyName("end")] + public DateTime End { get; set; } + [JsonPropertyName("reservantId")] + public Guid? ReservantId { get; set; } + [JsonPropertyName("tournamentId")] + public Guid? TournamentId { get; set; } + [JsonPropertyName("participantIds")] + public List? ParticipantIds { get; set; } + [JsonPropertyName("courtIds")] + public Guid CourtId { get; set; } + + public ReservationRejectedEvent(){} +} \ No newline at end of file diff --git a/Domain/Events/Reservation/TechnicalReservationEvent.cs b/Domain/Events/Reservation/TechnicalReservationEvent.cs new file mode 100644 index 0000000..36ddc13 --- /dev/null +++ b/Domain/Events/Reservation/TechnicalReservationEvent.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace PlayOfferService.Domain.Events.Reservation; + +public class TechnicalReservationEvent : BaseEvent, IRequest +{ + public TechnicalReservationEvent() { } +} \ No newline at end of file diff --git a/Domain/Models/Club.cs b/Domain/Models/Club.cs index 84fabae..6c44814 100644 --- a/Domain/Models/Club.cs +++ b/Domain/Models/Club.cs @@ -1,5 +1,6 @@ -using System.ComponentModel.DataAnnotations.Schema; using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Club; +using System.ComponentModel.DataAnnotations.Schema; namespace PlayOfferService.Domain.Models; @@ -7,6 +8,7 @@ public class Club { [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Id { get; set; } + public string Name { get; set; } public Status Status { get; set; } public void Apply(List baseEvents) @@ -16,7 +18,7 @@ public void Apply(List baseEvents) switch (baseEvent.EventType) { case EventType.TENNIS_CLUB_REGISTERED: - ApplyClubCreatedEvent((ClubCreatedEvent) baseEvent.EventData); + ApplyClubCreatedEvent((ClubCreatedEvent)baseEvent.EventData); break; case EventType.TENNIS_CLUB_LOCKED: ApplyClubLockedEvent(); @@ -27,29 +29,38 @@ public void Apply(List baseEvents) case EventType.TENNIS_CLUB_DELETED: ApplyClubDeletedEvent(); break; + case EventType.TENNIS_CLUB_NAME_CHANGED: + ApplyClubNameChangedEvent((ClubNameChangedEvent)baseEvent.EventData); + break; default: throw new ArgumentOutOfRangeException($"{nameof(baseEvent.EventType)} is not supported for the entity Club!"); } } } - + private void ApplyClubCreatedEvent(ClubCreatedEvent domainEvent) { Id = domainEvent.TennisClubId.Id; + Name = domainEvent.Name; } - + private void ApplyClubLockedEvent() { Status = Status.LOCKED; } - + private void ApplyClubUnlockedEvent() { Status = Status.ACTIVE; } - + private void ApplyClubDeletedEvent() { Status = Status.DELETED; } + + private void ApplyClubNameChangedEvent(ClubNameChangedEvent domainEvent) + { + Name = domainEvent.Name; + } } \ No newline at end of file diff --git a/Domain/Models/Court.cs b/Domain/Models/Court.cs new file mode 100644 index 0000000..8055c41 --- /dev/null +++ b/Domain/Models/Court.cs @@ -0,0 +1,45 @@ +using System.ComponentModel.DataAnnotations.Schema; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Court; + +namespace PlayOfferService.Domain.Models; + +public class Court +{ + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + public Guid ClubId { get; set; } + public string Name { get; set; } + + public void Apply(List baseEvents) + { + foreach (var baseEvent in baseEvents) + { + switch (baseEvent.EventType) + { + case EventType.CourtCreatedEvent: + ApplyCourtCreatedEvent(baseEvent); + break; + case EventType.CourtUpdatedEvent: + ApplyCourtUpdatedEvent(baseEvent); + break; + } + } + } + + private void ApplyCourtCreatedEvent(BaseEvent baseEvent) + { + var domainEvent = (CourtCreatedEvent)baseEvent.EventData; + + Id = baseEvent.EntityId; + ClubId = domainEvent.ClubId; + Name = domainEvent.Name; + } + + private void ApplyCourtUpdatedEvent(BaseEvent baseEvent) + { + var domainEvent = (CourtUpdatedEvent)baseEvent.EventData; + + Name = domainEvent.CourtName ?? Name; + } +} \ No newline at end of file diff --git a/Domain/Models/DTOs/ClubDto.cs b/Domain/Models/DTOs/ClubDto.cs new file mode 100644 index 0000000..796b6a3 --- /dev/null +++ b/Domain/Models/DTOs/ClubDto.cs @@ -0,0 +1,11 @@ +namespace PlayOfferService.Domain.Models; + +public class ClubDto : Club +{ + public ClubDto(Club club) + { + Id = club.Id; + Name = club.Name; + Status = club.Status; + } +} \ No newline at end of file diff --git a/Domain/Models/DTOs/CourtDto.cs b/Domain/Models/DTOs/CourtDto.cs new file mode 100644 index 0000000..fb4e5f6 --- /dev/null +++ b/Domain/Models/DTOs/CourtDto.cs @@ -0,0 +1,13 @@ +namespace PlayOfferService.Domain.Models; + +public class CourtDto +{ + public Guid Id { get; set; } + public string Name { get; set; } + + public CourtDto(Court court) + { + Id = court.Id; + Name = court.Name; + } +} \ No newline at end of file diff --git a/Domain/Models/PlayOfferDto.cs b/Domain/Models/DTOs/CreatePlayOfferDto.cs similarity index 58% rename from Domain/Models/PlayOfferDto.cs rename to Domain/Models/DTOs/CreatePlayOfferDto.cs index 71422e7..efa52c8 100644 --- a/Domain/Models/PlayOfferDto.cs +++ b/Domain/Models/DTOs/CreatePlayOfferDto.cs @@ -1,9 +1,7 @@ namespace PlayOfferService.Domain.Models; -public class PlayOfferDto +public class CreatePlayOfferDto { - public Guid ClubId { get; set; } - public Guid CreatorId { get; set; } public DateTime ProposedStartTime { get; set; } public DateTime ProposedEndTime { get; set; } } \ No newline at end of file diff --git a/Domain/Models/DTOs/MemberDto.cs b/Domain/Models/DTOs/MemberDto.cs new file mode 100644 index 0000000..cf0f8ff --- /dev/null +++ b/Domain/Models/DTOs/MemberDto.cs @@ -0,0 +1,21 @@ +namespace PlayOfferService.Domain.Models; + +public class MemberDto +{ + public Guid Id { get; set; } + + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + + public Status Status { get; set; } + + public MemberDto(Member member) + { + Id = member.Id; + Email = member.Email; + FirstName = member.FirstName; + LastName = member.LastName; + Status = member.Status; + } +} \ No newline at end of file diff --git a/Domain/Models/DTOs/PlayOfferDto.cs b/Domain/Models/DTOs/PlayOfferDto.cs new file mode 100644 index 0000000..8337942 --- /dev/null +++ b/Domain/Models/DTOs/PlayOfferDto.cs @@ -0,0 +1,27 @@ +namespace PlayOfferService.Domain.Models; + +public class PlayOfferDto +{ + public Guid Id { get; set; } + public ClubDto Club { get; set; } + public MemberDto Creator { get; set; } + public MemberDto? Opponent { get; set; } + public DateTime ProposedStartTime { get; set; } + public DateTime ProposedEndTime { get; set; } + public DateTime? AcceptedStartTime { get; set; } + public ReservationDto? Reservation { get; set; } + public bool IsCancelled { get; set; } + + public PlayOfferDto(PlayOffer playOffer, ClubDto club, MemberDto creator, MemberDto? opponent, ReservationDto? reservation) + { + Id = playOffer.Id; + Club = club; + Creator = creator; + Opponent = opponent; + ProposedStartTime = playOffer.ProposedStartTime; + ProposedEndTime = playOffer.ProposedEndTime; + AcceptedStartTime = playOffer.AcceptedStartTime; + Reservation = reservation; + IsCancelled = playOffer.IsCancelled; + } +} \ No newline at end of file diff --git a/Domain/Models/DTOs/ReservationDto.cs b/Domain/Models/DTOs/ReservationDto.cs new file mode 100644 index 0000000..1bf37b8 --- /dev/null +++ b/Domain/Models/DTOs/ReservationDto.cs @@ -0,0 +1,19 @@ +namespace PlayOfferService.Domain.Models; + +public class ReservationDto +{ + public Guid Id { get; set; } + public CourtDto Court { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public bool IsCancelled { get; set; } + + public ReservationDto(Reservation reservation, CourtDto court) + { + Id = reservation.Id; + Court = court; + StartTime = reservation.StartTime; + EndTime = reservation.EndTime; + IsCancelled = reservation.IsCancelled; + } +} \ No newline at end of file diff --git a/Domain/Models/JoinPlayOfferDto.cs b/Domain/Models/JoinPlayOfferDto.cs index 240c954..9d2ed93 100644 --- a/Domain/Models/JoinPlayOfferDto.cs +++ b/Domain/Models/JoinPlayOfferDto.cs @@ -3,6 +3,5 @@ namespace PlayOfferService.Domain.Models; public class JoinPlayOfferDto { public Guid PlayOfferId { get; set; } - public Guid OpponentId { get; set; } public DateTime AcceptedStartTime { get; set; } } \ No newline at end of file diff --git a/Domain/Models/Member.cs b/Domain/Models/Member.cs index cd75bdf..6153739 100644 --- a/Domain/Models/Member.cs +++ b/Domain/Models/Member.cs @@ -7,17 +7,20 @@ public class Member { public Guid Id { get; set; } public Guid ClubId { get; set; } - + + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public Status Status { get; set; } public void Apply(List baseEvents) { foreach (var baseEvent in baseEvents) - { switch (baseEvent.EventType) { case EventType.MEMBER_REGISTERED: - Apply((MemberCreatedEvent) baseEvent.EventData); + Apply((MemberCreatedEvent)baseEvent.EventData); break; case EventType.MEMBER_LOCKED: ApplyMemberLockedEvent(); @@ -28,29 +31,49 @@ public void Apply(List baseEvents) case EventType.MEMBER_DELETED: ApplyMemberDeletedEvent(); break; + case EventType.MEMBER_EMAIL_CHANGED: + ApplyMemberEmailChangedEvent((MemberEmailChangedEvent)baseEvent.EventData); + break; + case EventType.MEMBER_FULL_NAME_CHANGED: + ApplyMemberFullNameChangedEvent((MemberFullNameChangedEvent)baseEvent.EventData); + break; default: - throw new ArgumentOutOfRangeException($"{nameof(baseEvent.EventType)} is not supported for the entity Member!"); + throw new ArgumentOutOfRangeException( + $"{nameof(baseEvent.EventType)} is not supported for the entity Member!"); } - } } - + + private void ApplyMemberEmailChangedEvent(MemberEmailChangedEvent baseEventEventData) + { + Email = baseEventEventData.Email; + } + + private void ApplyMemberFullNameChangedEvent(MemberFullNameChangedEvent baseEventEventData) + { + FirstName = baseEventEventData.FullName.FirstName; + LastName = baseEventEventData.FullName.LastName; + } + private void Apply(MemberCreatedEvent domainEvent) { Id = domainEvent.MemberId.Id; + Email = domainEvent.Email; + FirstName = domainEvent.Name.FirstName; + LastName = domainEvent.Name.LastName; ClubId = domainEvent.TennisClubId.Id; Status = domainEvent.Status; } - + private void ApplyMemberLockedEvent() { Status = Status.LOCKED; } - + private void ApplyMemberUnlockedEvent() { Status = Status.ACTIVE; } - + private void ApplyMemberDeletedEvent() { Status = Status.DELETED; diff --git a/Domain/Models/PlayOffer.cs b/Domain/Models/PlayOffer.cs index 4032f7e..41d31a7 100644 --- a/Domain/Models/PlayOffer.cs +++ b/Domain/Models/PlayOffer.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations.Schema; using PlayOfferService.Domain.Events; using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Events.Reservation; namespace PlayOfferService.Domain.Models; @@ -34,8 +35,12 @@ public void Apply(List baseEvents) case EventType.PLAYOFFER_CANCELLED: ApplyPlayOfferCancelledEvent(); break; - case EventType.PLAYOFFER_RESERVATION_CREATED: - throw new NotImplementedException(); + case EventType.PLAYOFFER_RESERVATION_ADDED: + ApplyPlayOfferReservationAddedEvent((PlayOfferReservationAddedEvent)baseEvent.EventData); + break; + case EventType.PLAYOFFER_OPPONENT_REMOVED: + ApplyPlayOfferOpponentRemovedEvent(); + break; default: throw new ArgumentOutOfRangeException($"{nameof(baseEvent.EventType)} is not supported for the entity Playoffer!"); } @@ -62,4 +67,15 @@ private void ApplyPlayOfferCancelledEvent() { IsCancelled = true; } + + private void ApplyPlayOfferReservationAddedEvent(PlayOfferReservationAddedEvent domainEvent) + { + ReservationId = domainEvent.ReservationId; + } + + private void ApplyPlayOfferOpponentRemovedEvent() + { + OpponentId = null; + AcceptedStartTime = null; + } } \ No newline at end of file diff --git a/Domain/Models/Reservation.cs b/Domain/Models/Reservation.cs index fb39936..ad37f01 100644 --- a/Domain/Models/Reservation.cs +++ b/Domain/Models/Reservation.cs @@ -1,4 +1,6 @@ using System.ComponentModel.DataAnnotations.Schema; +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Reservation; namespace PlayOfferService.Domain.Models; @@ -6,4 +8,44 @@ public class Reservation { [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Id { get; set; } + public Guid CourtId { get; set; } + public Guid? ReservantId { get; set; } + public DateTime StartTime { get; set; } + public DateTime EndTime { get; set; } + public List? ParticipantsIds { get; set; } = []; + public bool IsCancelled { get; set; } + + public void Apply (List baseEvents) + { + foreach (var baseEvent in baseEvents) + { + switch (baseEvent.EventType) + { + case EventType.ReservationCreatedEvent: + ApplyReservationCreatedEvent(baseEvent); + break; + case EventType.ReservationCancelledEvent: + ApplyReservationCancelledEvent(); + break; + } + } + } + + private void ApplyReservationCancelledEvent() + { + IsCancelled = true; + } + + private void ApplyReservationCreatedEvent(BaseEvent baseEvent) + { + var domainEvent = (ReservationCreatedEvent) baseEvent.EventData; + + Id = baseEvent.EntityId; + CourtId = domainEvent.CourtId; + ReservantId = domainEvent.ReservantId; + StartTime = domainEvent.Start; + EndTime = domainEvent.End; + ParticipantsIds = domainEvent.ParticipantIds ?? []; + IsCancelled = false; + } } \ No newline at end of file diff --git a/Domain/Repositories/ClubRepository.cs b/Domain/Repositories/ClubRepository.cs index 3770c4b..f2c69df 100644 --- a/Domain/Repositories/ClubRepository.cs +++ b/Domain/Repositories/ClubRepository.cs @@ -14,6 +14,11 @@ public ClubRepository(DbReadContext context) { _context = context; } + + public virtual async Task Update() + { + await _context.SaveChangesAsync(); + } public virtual async Task GetClubById(Guid clubId) { @@ -26,33 +31,14 @@ public ClubRepository(DbReadContext context) return club.First(); } - - public async Task UpdateEntityAsync(BaseEvent baseEvent) + + public virtual async Task> GetAllClubs() { - Console.WriteLine("MemberRepo received event: " + baseEvent.EventType); - var appliedEvents = await _context.AppliedEvents - .Where(e => e.EntityId == baseEvent.EntityId) - .ToListAsync(); - - if (appliedEvents.Any(e => e.EventId == baseEvent.EventId)) - { - Console.WriteLine("Event already applied, skipping"); - return; - } - - if (baseEvent.EventType == EventType.TENNIS_CLUB_REGISTERED) - { - var newClub = new Club(); - newClub.Apply([baseEvent]); - _context.Clubs.Add(newClub); - } - else - { - var existingClub = await GetClubById(baseEvent.EntityId); - existingClub!.Apply([baseEvent]); - } - - _context.AppliedEvents.Add(baseEvent); - await _context.SaveChangesAsync(); + return await _context.Clubs.ToListAsync(); + } + + public virtual void CreateClub(Club club) + { + _context.Clubs.Add(club); } } diff --git a/Domain/Repositories/CourtRepository.cs b/Domain/Repositories/CourtRepository.cs new file mode 100644 index 0000000..933da7a --- /dev/null +++ b/Domain/Repositories/CourtRepository.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Domain.Repositories; + +public class CourtRepository +{ + private readonly DbReadContext _context; + + public CourtRepository(){} + + public CourtRepository(DbReadContext context) + { + _context = context; + } + + public async Task> GetAllCourts() + { + return await _context.Courts.ToListAsync(); + } + + public virtual async Task GetCourtById(Guid? courtId) + { + var courts = await _context.Courts + .Where(e => e.Id == courtId) + .ToListAsync(); + + var allCourts = await _context.Courts.ToListAsync(); + + if (courts.Count == 0) + return null; + + return courts.First(); + } + + public virtual void CreateCourt(Court court) + { + _context.Courts.Add(court); + } + + public virtual async Task Update() + { + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/Domain/Repositories/MemberRepository.cs b/Domain/Repositories/MemberRepository.cs index d6245f4..d928a41 100644 --- a/Domain/Repositories/MemberRepository.cs +++ b/Domain/Repositories/MemberRepository.cs @@ -14,6 +14,11 @@ public MemberRepository(DbReadContext context) { _context = context; } + + public virtual async Task> GetAllMembers() + { + return await _context.Members.ToListAsync(); + } public virtual async Task GetMemberById(Guid? memberId) { @@ -26,33 +31,34 @@ public MemberRepository(DbReadContext context) return member.First(); } - - public async Task UpdateEntityAsync(BaseEvent baseEvent) + + public virtual async Task> GetMemberByName(string creatorName) { - Console.WriteLine("MemberRepository received event: " + baseEvent.EventType); - var appliedEvents = await _context.AppliedEvents - .Where(e => e.EntityId == baseEvent.EntityId) - .ToListAsync(); - - if (appliedEvents.Any(e => e.EventId == baseEvent.EventId)) - { - Console.WriteLine("Event already applied, skipping"); - return; - } - - if (baseEvent.EventType == EventType.MEMBER_REGISTERED) + var firstAndLastName = creatorName.Split(" "); + + var members = new List(); + if (firstAndLastName.Length == 2) { - var newMember = new Member(); - newMember.Apply([baseEvent]); - _context.Members.Add(newMember); - } - else + members = await _context.Members + .Where(e => e.FirstName.ToLower().Contains(firstAndLastName[0].ToLower()) && e.LastName.ToLower().Contains(firstAndLastName[1].ToLower())) + .ToListAsync(); + } else if (firstAndLastName.Length == 1) { - var existingMember = await GetMemberById(baseEvent.EntityId); - existingMember?.Apply([baseEvent]); + members = await _context.Members + .Where(e => e.FirstName.ToLower().Contains(firstAndLastName[0].ToLower()) || e.LastName.ToLower().Contains(firstAndLastName[0].ToLower())) + .ToListAsync(); } + + return members; + } + + public virtual void CreateMember(Member member) + { + _context.Members.Add(member); + } - _context.AppliedEvents.Add(baseEvent); + public virtual async Task Update() + { await _context.SaveChangesAsync(); } } \ No newline at end of file diff --git a/Domain/Repositories/PlayOfferRepository.cs b/Domain/Repositories/PlayOfferRepository.cs index 8219d7c..3bf171d 100644 --- a/Domain/Repositories/PlayOfferRepository.cs +++ b/Domain/Repositories/PlayOfferRepository.cs @@ -16,7 +16,8 @@ public PlayOfferRepository(DbReadContext context) public async Task> GetPlayOffersByIds( Guid? playOfferId, Guid? creatorId = null, - Guid? clubId = null) + Guid? clubId = null, + Guid? opponentId = null) { var playOffers = await _context.PlayOffers .ToListAsync(); @@ -25,57 +26,46 @@ public async Task> GetPlayOffersByIds( e != null && (!playOfferId.HasValue || e.Id == playOfferId) && (!creatorId.HasValue || e.CreatorId == creatorId) - && (!clubId.HasValue || e.ClubId == clubId)).ToList(); + && (!clubId.HasValue || e.ClubId == clubId) + && (!opponentId.HasValue || e.OpponentId == opponentId) + ).ToList(); return playOffers; } - - public async Task UpdateEntityAsync(BaseEvent baseEvent) + + public async Task> GetPlayOffersByParticipantId(Guid participantId) { - Console.WriteLine("PlayOfferRepository received event: " + baseEvent.EventType); - var appliedEvents = await _context.AppliedEvents - .Where(e => e.EntityId == baseEvent.EntityId) - .ToListAsync(); - - if (appliedEvents.Any(e => e.EventId == baseEvent.EventId)) - { - Console.WriteLine("Event already applied, skipping"); - return; - } + var playOffers = await _context.PlayOffers.Where(e => e.CreatorId == participantId || e.OpponentId == participantId).ToListAsync(); - switch (baseEvent.EventType) - { - case EventType.PLAYOFFER_CANCELLED: - await CancelPlayOffer(baseEvent); - break; - case EventType.PLAYOFFER_CREATED: - CreatePlayOffer(baseEvent); - break; - case EventType.PLAYOFFER_JOINED: - await JoinPlayOffer(baseEvent); - break; - } - - _context.AppliedEvents.Add(baseEvent); - await _context.SaveChangesAsync(); + return playOffers; } + + public async Task GetPlayOfferByEventId(Guid eventId) + { + var playOffer = await _context.AppliedEvents + .Where(e => e.EventId == eventId) + .Select(e => e.EntityId) + .SelectMany(e => _context.PlayOffers.Where(p => p.Id == e)) + .FirstOrDefaultAsync(); - private async Task CancelPlayOffer(BaseEvent baseEvent) + return playOffer; + } + + public async Task GetPlayOfferByReservationId(Guid reservationId) { - var existingPlayOffer = (await GetPlayOffersByIds(baseEvent.EntityId)).First(); - existingPlayOffer.Apply([baseEvent]); + return await _context.PlayOffers + .Where(e => e.ReservationId == reservationId) + .FirstOrDefaultAsync(); } - private void CreatePlayOffer(BaseEvent baseEvent) + + public async Task Update() { - var newPlayOffer = new PlayOffer(); - newPlayOffer.Apply([baseEvent]); - _context.PlayOffers.Add(newPlayOffer); + await _context.SaveChangesAsync(); } - - private async Task JoinPlayOffer(BaseEvent baseEvent) + + public void CreatePlayOffer(PlayOffer playOffer) { - var existingPlayOffer = (await GetPlayOffersByIds(baseEvent.EntityId)).First(); - existingPlayOffer.Apply([baseEvent]); + _context.PlayOffers.Add(playOffer); } } \ No newline at end of file diff --git a/Domain/Repositories/ReadEventRepository.cs b/Domain/Repositories/ReadEventRepository.cs new file mode 100644 index 0000000..78d955d --- /dev/null +++ b/Domain/Repositories/ReadEventRepository.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using PlayOfferService.Domain.Events; + +namespace PlayOfferService.Domain.Repositories; + +public class ReadEventRepository +{ + private readonly DbReadContext _context; + + public ReadEventRepository(){} + + public ReadEventRepository(DbReadContext context) + { + _context = context; + } + + public virtual async Task GetEventById(Guid eventId) + { + var events = await _context.AppliedEvents + .Where(e => e.EventId == eventId) + .ToListAsync(); + + if (events.Count == 0) + return null; + + return events.First(); + } + + public virtual async Task AppendEvent(BaseEvent baseEvent) + { + _context.AppliedEvents.Add(baseEvent); + } + + public virtual async Task Update() + { + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/Domain/Repositories/ReservationRepository.cs b/Domain/Repositories/ReservationRepository.cs new file mode 100644 index 0000000..ab98215 --- /dev/null +++ b/Domain/Repositories/ReservationRepository.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Domain.Repositories; + +public class ReservationRepository +{ + private readonly DbReadContext _context; + + public ReservationRepository(){} + + public ReservationRepository(DbReadContext context) + { + _context = context; + } + + public async Task> GetAllReservations() + { + return await _context.Reservations.ToListAsync(); + } + + public virtual async Task GetReservationById(Guid? reservationId) + { + var reservation = await _context.Reservations + .Where(e => e.Id == reservationId) + .ToListAsync(); + + if (reservation.Count == 0) + return null; + + return reservation.First(); + } + + public virtual void CreateReservation(Reservation reservation) + { + _context.Reservations.Add(reservation); + } + + public virtual async Task Update() + { + await _context.SaveChangesAsync(); + } +} \ No newline at end of file diff --git a/Domain/Repositories/WriteEventRepository.cs b/Domain/Repositories/WriteEventRepository.cs new file mode 100644 index 0000000..28603e2 --- /dev/null +++ b/Domain/Repositories/WriteEventRepository.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using PlayOfferService.Domain.Events; + +namespace PlayOfferService.Domain.Repositories; + +public class WriteEventRepository +{ + private readonly DbWriteContext _context; + + public WriteEventRepository() { } + + public WriteEventRepository(DbWriteContext context) + { + _context = context; + } + + public virtual async Task GetEventById(Guid eventId) + { + var events = await _context.Events + .Where(e => e.EventId == eventId) + .ToListAsync(); + + if (events.Count == 0) + return null; + + return events.First(); + } + + public virtual async Task> GetEventByEntityId(Guid entityId) + { + var events = await _context.Events + .Where(e => e.EntityId == entityId) + .ToListAsync(); + + return events; + } + + public virtual async Task AppendEvent(BaseEvent baseEvent) + { + _context.Events.Add(baseEvent); + } + + public virtual async Task Update() + { + await _context.SaveChangesAsync(); + } + + internal int GetEventCount(Guid playOfferId) + { + return _context.Events.Count(e => e.EntityId == playOfferId); + } + + internal IDbContextTransaction StartTransaction() + { + return _context.Database.BeginTransaction(); + } +} \ No newline at end of file diff --git a/Domain/ValueObjects/Restriction.cs b/Domain/ValueObjects/Restriction.cs new file mode 100644 index 0000000..351b5ff --- /dev/null +++ b/Domain/ValueObjects/Restriction.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace PlayOfferService.Domain.ValueObjects; + +public class Restriction +{ + [JsonPropertyName("weekday")] + public Weekday Weekday { get; set; } + [JsonPropertyName("timeRanges")] + public List TimeRanges { get; set; } +} \ No newline at end of file diff --git a/Domain/Models/Status.cs b/Domain/ValueObjects/Status.cs similarity index 100% rename from Domain/Models/Status.cs rename to Domain/ValueObjects/Status.cs diff --git a/Domain/ValueObjects/TimeRange.cs b/Domain/ValueObjects/TimeRange.cs new file mode 100644 index 0000000..ce920ba --- /dev/null +++ b/Domain/ValueObjects/TimeRange.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace PlayOfferService.Domain.ValueObjects; + +public class TimeRange +{ + [JsonPropertyName("start")] + public DateTime Start { get; } + [JsonPropertyName("end")] + public DateTime End { get; } +} \ No newline at end of file diff --git a/Domain/ValueObjects/Weekday.cs b/Domain/ValueObjects/Weekday.cs new file mode 100644 index 0000000..313a2d4 --- /dev/null +++ b/Domain/ValueObjects/Weekday.cs @@ -0,0 +1,12 @@ +namespace PlayOfferService.Domain.ValueObjects; + +public enum Weekday +{ + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday +} \ No newline at end of file diff --git a/Migrations/20240606085906_ExpandedReservationModel.Designer.cs b/Migrations/20240606085906_ExpandedReservationModel.Designer.cs new file mode 100644 index 0000000..446da24 --- /dev/null +++ b/Migrations/20240606085906_ExpandedReservationModel.Designer.cs @@ -0,0 +1,221 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PlayOfferService.Domain; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + [DbContext(typeof(DbReadContext))] + [Migration("20240606085906_ExpandedReservationModel")] + partial class ExpandedReservationModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PlayOfferService.Domain.Events.BaseEvent", b => + { + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("eventId"); + + b.Property("CorrelationId") + .HasColumnType("uuid") + .HasColumnName("correlationId"); + + b.Property("EntityId") + .HasColumnType("uuid") + .HasColumnName("entityId"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("entityType"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventData"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventType"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("EventId") + .HasName("pK_events"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_clubs"); + + b.ToTable("clubs", (string)null); + + b.HasData( + new + { + Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_members"); + + b.ToTable("members", (string)null); + + b.HasData( + new + { + Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }, + new + { + Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AcceptedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("acceptedStartTime"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("creatorId"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property("OpponentId") + .HasColumnType("uuid") + .HasColumnName("opponentId"); + + b.Property("ProposedEndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedEndTime"); + + b.Property("ProposedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedStartTime"); + + b.Property("ReservationId") + .HasColumnType("uuid") + .HasColumnName("reservationId"); + + b.HasKey("Id") + .HasName("pK_playOffers"); + + b.ToTable("playOffers", (string)null); + + b.HasData( + new + { + Id = new Guid("781d0a5e-608d-4765-be03-4e9759fbf8be"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + IsCancelled = false, + ProposedEndTime = new DateTime(2024, 6, 6, 9, 59, 5, 597, DateTimeKind.Utc).AddTicks(5737), + ProposedStartTime = new DateTime(2024, 6, 6, 8, 59, 5, 597, DateTimeKind.Utc).AddTicks(5734) + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("CourtIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("courtIds"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + + b.HasKey("Id") + .HasName("pK_reservations"); + + b.ToTable("reservations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20240606085906_ExpandedReservationModel.cs b/Migrations/20240606085906_ExpandedReservationModel.cs new file mode 100644 index 0000000..a78f780 --- /dev/null +++ b/Migrations/20240606085906_ExpandedReservationModel.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + /// + public partial class ExpandedReservationModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("6fdd5f06-9099-4e7c-ad94-e184df54676b")); + + migrationBuilder.AddColumn>( + name: "courtIds", + table: "reservations", + type: "uuid[]", + nullable: false); + + migrationBuilder.AddColumn( + name: "endTime", + table: "reservations", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "isCancelled", + table: "reservations", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn>( + name: "participantsIds", + table: "reservations", + type: "uuid[]", + nullable: false); + + migrationBuilder.AddColumn( + name: "reservantId", + table: "reservations", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "startTime", + table: "reservations", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("781d0a5e-608d-4765-be03-4e9759fbf8be"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 9, 59, 5, 597, DateTimeKind.Utc).AddTicks(5737), new DateTime(2024, 6, 6, 8, 59, 5, 597, DateTimeKind.Utc).AddTicks(5734), null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("781d0a5e-608d-4765-be03-4e9759fbf8be")); + + migrationBuilder.DropColumn( + name: "courtIds", + table: "reservations"); + + migrationBuilder.DropColumn( + name: "endTime", + table: "reservations"); + + migrationBuilder.DropColumn( + name: "isCancelled", + table: "reservations"); + + migrationBuilder.DropColumn( + name: "participantsIds", + table: "reservations"); + + migrationBuilder.DropColumn( + name: "reservantId", + table: "reservations"); + + migrationBuilder.DropColumn( + name: "startTime", + table: "reservations"); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("6fdd5f06-9099-4e7c-ad94-e184df54676b"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 2, 12, 39, 23, 865, DateTimeKind.Utc).AddTicks(7636), new DateTime(2024, 6, 2, 11, 39, 23, 865, DateTimeKind.Utc).AddTicks(7634), null }); + } + } +} diff --git a/Migrations/20240606112435_AddedCourtModel.Designer.cs b/Migrations/20240606112435_AddedCourtModel.Designer.cs new file mode 100644 index 0000000..2b32c38 --- /dev/null +++ b/Migrations/20240606112435_AddedCourtModel.Designer.cs @@ -0,0 +1,242 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PlayOfferService.Domain; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + [DbContext(typeof(DbReadContext))] + [Migration("20240606112435_AddedCourtModel")] + partial class AddedCourtModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PlayOfferService.Domain.Events.BaseEvent", b => + { + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("eventId"); + + b.Property("CorrelationId") + .HasColumnType("uuid") + .HasColumnName("correlationId"); + + b.Property("EntityId") + .HasColumnType("uuid") + .HasColumnName("entityId"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("entityType"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventData"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventType"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("EventId") + .HasName("pK_events"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_clubs"); + + b.ToTable("clubs", (string)null); + + b.HasData( + new + { + Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Court", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pK_courts"); + + b.ToTable("courts", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_members"); + + b.ToTable("members", (string)null); + + b.HasData( + new + { + Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }, + new + { + Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AcceptedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("acceptedStartTime"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("creatorId"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property("OpponentId") + .HasColumnType("uuid") + .HasColumnName("opponentId"); + + b.Property("ProposedEndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedEndTime"); + + b.Property("ProposedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedStartTime"); + + b.Property("ReservationId") + .HasColumnType("uuid") + .HasColumnName("reservationId"); + + b.HasKey("Id") + .HasName("pK_playOffers"); + + b.ToTable("playOffers", (string)null); + + b.HasData( + new + { + Id = new Guid("92ed1e90-1031-4999-8958-c6140cf4ea56"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + IsCancelled = false, + ProposedEndTime = new DateTime(2024, 6, 6, 12, 24, 35, 25, DateTimeKind.Utc).AddTicks(9886), + ProposedStartTime = new DateTime(2024, 6, 6, 11, 24, 35, 25, DateTimeKind.Utc).AddTicks(9883) + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("CourtIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("courtIds"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + + b.HasKey("Id") + .HasName("pK_reservations"); + + b.ToTable("reservations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20240606112435_AddedCourtModel.cs b/Migrations/20240606112435_AddedCourtModel.cs new file mode 100644 index 0000000..94ef82a --- /dev/null +++ b/Migrations/20240606112435_AddedCourtModel.cs @@ -0,0 +1,55 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + /// + public partial class AddedCourtModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("781d0a5e-608d-4765-be03-4e9759fbf8be")); + + migrationBuilder.CreateTable( + name: "courts", + columns: table => new + { + id = table.Column(type: "uuid", nullable: false), + clubId = table.Column(type: "uuid", nullable: false), + name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pK_courts", x => x.id); + }); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("92ed1e90-1031-4999-8958-c6140cf4ea56"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 12, 24, 35, 25, DateTimeKind.Utc).AddTicks(9886), new DateTime(2024, 6, 6, 11, 24, 35, 25, DateTimeKind.Utc).AddTicks(9883), null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "courts"); + + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("92ed1e90-1031-4999-8958-c6140cf4ea56")); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("781d0a5e-608d-4765-be03-4e9759fbf8be"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 9, 59, 5, 597, DateTimeKind.Utc).AddTicks(5737), new DateTime(2024, 6, 6, 8, 59, 5, 597, DateTimeKind.Utc).AddTicks(5734), null }); + } + } +} diff --git a/Migrations/20240606181003_ReservationChangedModel.Designer.cs b/Migrations/20240606181003_ReservationChangedModel.Designer.cs new file mode 100644 index 0000000..2cc7d3a --- /dev/null +++ b/Migrations/20240606181003_ReservationChangedModel.Designer.cs @@ -0,0 +1,241 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PlayOfferService.Domain; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + [DbContext(typeof(DbReadContext))] + [Migration("20240606181003_ReservationChangedModel")] + partial class ReservationChangedModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PlayOfferService.Domain.Events.BaseEvent", b => + { + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("eventId"); + + b.Property("CorrelationId") + .HasColumnType("uuid") + .HasColumnName("correlationId"); + + b.Property("EntityId") + .HasColumnType("uuid") + .HasColumnName("entityId"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("entityType"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventData"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventType"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("EventId") + .HasName("pK_events"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_clubs"); + + b.ToTable("clubs", (string)null); + + b.HasData( + new + { + Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Court", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pK_courts"); + + b.ToTable("courts", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_members"); + + b.ToTable("members", (string)null); + + b.HasData( + new + { + Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }, + new + { + Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AcceptedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("acceptedStartTime"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("creatorId"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property("OpponentId") + .HasColumnType("uuid") + .HasColumnName("opponentId"); + + b.Property("ProposedEndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedEndTime"); + + b.Property("ProposedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedStartTime"); + + b.Property("ReservationId") + .HasColumnType("uuid") + .HasColumnName("reservationId"); + + b.HasKey("Id") + .HasName("pK_playOffers"); + + b.ToTable("playOffers", (string)null); + + b.HasData( + new + { + Id = new Guid("c0f37db0-4c51-4bd3-86ab-48d6c78eb9c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + IsCancelled = false, + ProposedEndTime = new DateTime(2024, 6, 6, 19, 10, 3, 28, DateTimeKind.Utc).AddTicks(4637), + ProposedStartTime = new DateTime(2024, 6, 6, 18, 10, 3, 28, DateTimeKind.Utc).AddTicks(4634) + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("CourtIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("courtIds"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + + b.HasKey("Id") + .HasName("pK_reservations"); + + b.ToTable("reservations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20240606181003_ReservationChangedModel.cs b/Migrations/20240606181003_ReservationChangedModel.cs new file mode 100644 index 0000000..5d38366 --- /dev/null +++ b/Migrations/20240606181003_ReservationChangedModel.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + /// + public partial class ReservationChangedModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("92ed1e90-1031-4999-8958-c6140cf4ea56")); + + migrationBuilder.AlterColumn>( + name: "participantsIds", + table: "reservations", + type: "uuid[]", + nullable: true, + oldClrType: typeof(List), + oldType: "uuid[]"); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("c0f37db0-4c51-4bd3-86ab-48d6c78eb9c2"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 19, 10, 3, 28, DateTimeKind.Utc).AddTicks(4637), new DateTime(2024, 6, 6, 18, 10, 3, 28, DateTimeKind.Utc).AddTicks(4634), null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("c0f37db0-4c51-4bd3-86ab-48d6c78eb9c2")); + + migrationBuilder.AlterColumn>( + name: "participantsIds", + table: "reservations", + type: "uuid[]", + nullable: false, + oldClrType: typeof(List), + oldType: "uuid[]", + oldNullable: true); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("92ed1e90-1031-4999-8958-c6140cf4ea56"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 12, 24, 35, 25, DateTimeKind.Utc).AddTicks(9886), new DateTime(2024, 6, 6, 11, 24, 35, 25, DateTimeKind.Utc).AddTicks(9883), null }); + } + } +} diff --git a/Migrations/20240607112446_memberExpansion.Designer.cs b/Migrations/20240607112446_memberExpansion.Designer.cs new file mode 100644 index 0000000..5cf0c7b --- /dev/null +++ b/Migrations/20240607112446_memberExpansion.Designer.cs @@ -0,0 +1,259 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PlayOfferService.Domain; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + [DbContext(typeof(DbReadContext))] + [Migration("20240607112446_memberExpansion")] + partial class memberExpansion + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PlayOfferService.Domain.Events.BaseEvent", b => + { + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("eventId"); + + b.Property("CorrelationId") + .HasColumnType("uuid") + .HasColumnName("correlationId"); + + b.Property("EntityId") + .HasColumnType("uuid") + .HasColumnName("entityId"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("entityType"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventData"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventType"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("EventId") + .HasName("pK_events"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_clubs"); + + b.ToTable("clubs", (string)null); + + b.HasData( + new + { + Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Name = "Test Club", + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Court", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pK_courts"); + + b.ToTable("courts", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Email") + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .HasColumnType("text") + .HasColumnName("firstName"); + + b.Property("LastName") + .HasColumnType("text") + .HasColumnName("lastName"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_members"); + + b.ToTable("members", (string)null); + + b.HasData( + new + { + Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }, + new + { + Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AcceptedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("acceptedStartTime"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("creatorId"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property("OpponentId") + .HasColumnType("uuid") + .HasColumnName("opponentId"); + + b.Property("ProposedEndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedEndTime"); + + b.Property("ProposedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedStartTime"); + + b.Property("ReservationId") + .HasColumnType("uuid") + .HasColumnName("reservationId"); + + b.HasKey("Id") + .HasName("pK_playOffers"); + + b.ToTable("playOffers", (string)null); + + b.HasData( + new + { + Id = new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + IsCancelled = false, + ProposedEndTime = new DateTime(2024, 6, 7, 12, 24, 44, 893, DateTimeKind.Utc).AddTicks(6493), + ProposedStartTime = new DateTime(2024, 6, 7, 11, 24, 44, 893, DateTimeKind.Utc).AddTicks(6491) + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property>("CourtIds") + .IsRequired() + .HasColumnType("uuid[]") + .HasColumnName("courtIds"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + + b.HasKey("Id") + .HasName("pK_reservations"); + + b.ToTable("reservations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20240607112446_memberExpansion.cs b/Migrations/20240607112446_memberExpansion.cs new file mode 100644 index 0000000..9f923d2 --- /dev/null +++ b/Migrations/20240607112446_memberExpansion.cs @@ -0,0 +1,101 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + /// + public partial class memberExpansion : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("c0f37db0-4c51-4bd3-86ab-48d6c78eb9c2")); + + migrationBuilder.AddColumn( + name: "email", + table: "members", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "firstName", + table: "members", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "lastName", + table: "members", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "name", + table: "clubs", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.UpdateData( + table: "clubs", + keyColumn: "id", + keyValue: new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + column: "name", + value: "Test Club"); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { null, null, null }); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { null, null, null }); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 7, 12, 24, 44, 893, DateTimeKind.Utc).AddTicks(6493), new DateTime(2024, 6, 7, 11, 24, 44, 893, DateTimeKind.Utc).AddTicks(6491), null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6")); + + migrationBuilder.DropColumn( + name: "email", + table: "members"); + + migrationBuilder.DropColumn( + name: "firstName", + table: "members"); + + migrationBuilder.DropColumn( + name: "lastName", + table: "members"); + + migrationBuilder.DropColumn( + name: "name", + table: "clubs"); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("c0f37db0-4c51-4bd3-86ab-48d6c78eb9c2"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 6, 19, 10, 3, 28, DateTimeKind.Utc).AddTicks(4637), new DateTime(2024, 6, 6, 18, 10, 3, 28, DateTimeKind.Utc).AddTicks(4634), null }); + } + } +} diff --git a/Migrations/20240622092733_ReservationChangedCourtId.Designer.cs b/Migrations/20240622092733_ReservationChangedCourtId.Designer.cs new file mode 100644 index 0000000..6b652d1 --- /dev/null +++ b/Migrations/20240622092733_ReservationChangedCourtId.Designer.cs @@ -0,0 +1,267 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using PlayOfferService.Domain; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + [DbContext(typeof(DbReadContext))] + [Migration("20240622092733_ReservationChangedCourtId")] + partial class ReservationChangedCourtId + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("PlayOfferService.Domain.Events.BaseEvent", b => + { + b.Property("EventId") + .HasColumnType("uuid") + .HasColumnName("eventId"); + + b.Property("CorrelationId") + .HasColumnType("uuid") + .HasColumnName("correlationId"); + + b.Property("EntityId") + .HasColumnType("uuid") + .HasColumnName("entityId"); + + b.Property("EntityType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("entityType"); + + b.Property("EventData") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventData"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eventType"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone") + .HasColumnName("timestamp"); + + b.HasKey("EventId") + .HasName("pK_events"); + + b.ToTable("events", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_clubs"); + + b.ToTable("clubs", (string)null); + + b.HasData( + new + { + Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Name = "Test Club", + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Court", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pK_courts"); + + b.ToTable("courts", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("firstName"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("lastName"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pK_members"); + + b.ToTable("members", (string)null); + + b.HasData( + new + { + Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Email = "hans@müller.de", + FirstName = "Hans", + LastName = "Müller", + Status = 0 + }, + new + { + Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Email = "friedrich@bäcker.at", + FirstName = "Friedrich", + LastName = "Bäcker", + Status = 0 + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("AcceptedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("acceptedStartTime"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("creatorId"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property("OpponentId") + .HasColumnType("uuid") + .HasColumnName("opponentId"); + + b.Property("ProposedEndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedEndTime"); + + b.Property("ProposedStartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("proposedStartTime"); + + b.Property("ReservationId") + .HasColumnType("uuid") + .HasColumnName("reservationId"); + + b.HasKey("Id") + .HasName("pK_playOffers"); + + b.ToTable("playOffers", (string)null); + + b.HasData( + new + { + Id = new Guid("a4e3d4e5-5215-4094-a333-ce3ee8154675"), + ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + IsCancelled = false, + ProposedEndTime = new DateTime(2024, 6, 22, 10, 27, 32, 806, DateTimeKind.Utc).AddTicks(9696), + ProposedStartTime = new DateTime(2024, 6, 22, 9, 27, 32, 806, DateTimeKind.Utc).AddTicks(9693) + }); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("CourtId") + .HasColumnType("uuid") + .HasColumnName("courtId"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + + b.HasKey("Id") + .HasName("pK_reservations"); + + b.ToTable("reservations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20240622092733_ReservationChangedCourtId.cs b/Migrations/20240622092733_ReservationChangedCourtId.cs new file mode 100644 index 0000000..48ac239 --- /dev/null +++ b/Migrations/20240622092733_ReservationChangedCourtId.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PlayOfferService.Migrations +{ + /// + public partial class ReservationChangedCourtId : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6")); + + migrationBuilder.DropColumn( + name: "courtIds", + table: "reservations"); + + migrationBuilder.AddColumn( + name: "courtId", + table: "reservations", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AlterColumn( + name: "lastName", + table: "members", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "firstName", + table: "members", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "email", + table: "members", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { "hans@müller.de", "Hans", "Müller" }); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { "friedrich@bäcker.at", "Friedrich", "Bäcker" }); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("a4e3d4e5-5215-4094-a333-ce3ee8154675"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 22, 10, 27, 32, 806, DateTimeKind.Utc).AddTicks(9696), new DateTime(2024, 6, 22, 9, 27, 32, 806, DateTimeKind.Utc).AddTicks(9693), null }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "playOffers", + keyColumn: "id", + keyValue: new Guid("a4e3d4e5-5215-4094-a333-ce3ee8154675")); + + migrationBuilder.DropColumn( + name: "courtId", + table: "reservations"); + + migrationBuilder.AddColumn>( + name: "courtIds", + table: "reservations", + type: "uuid[]", + nullable: false); + + migrationBuilder.AlterColumn( + name: "lastName", + table: "members", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "firstName", + table: "members", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "email", + table: "members", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { null, null, null }); + + migrationBuilder.UpdateData( + table: "members", + keyColumn: "id", + keyValue: new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), + columns: new[] { "email", "firstName", "lastName" }, + values: new object[] { null, null, null }); + + migrationBuilder.InsertData( + table: "playOffers", + columns: new[] { "id", "acceptedStartTime", "clubId", "creatorId", "isCancelled", "opponentId", "proposedEndTime", "proposedStartTime", "reservationId" }, + values: new object[] { new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6"), null, new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), false, null, new DateTime(2024, 6, 7, 12, 24, 44, 893, DateTimeKind.Utc).AddTicks(6493), new DateTime(2024, 6, 7, 11, 24, 44, 893, DateTimeKind.Utc).AddTicks(6491), null }); + } + } +} diff --git a/Migrations/DbReadContextModelSnapshot.cs b/Migrations/DbReadContextModelSnapshot.cs index b8a6443..33815e0 100644 --- a/Migrations/DbReadContextModelSnapshot.cs +++ b/Migrations/DbReadContextModelSnapshot.cs @@ -1,5 +1,6 @@ // using System; +using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -61,12 +62,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("events", (string)null); }); - modelBuilder.Entity("PlayOfferService.Models.Club", b => + modelBuilder.Entity("PlayOfferService.Domain.Models.Club", b => { b.Property("Id") .HasColumnType("uuid") .HasColumnName("id"); + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + b.Property("Status") .HasColumnType("integer") .HasColumnName("status"); @@ -80,11 +86,33 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Name = "Test Club", Status = 0 }); }); - modelBuilder.Entity("PlayOfferService.Models.Member", b => + modelBuilder.Entity("PlayOfferService.Domain.Models.Court", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClubId") + .HasColumnType("uuid") + .HasColumnName("clubId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pK_courts"); + + b.ToTable("courts", (string)null); + }); + + modelBuilder.Entity("PlayOfferService.Domain.Models.Member", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -95,6 +123,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("clubId"); + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("firstName"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("lastName"); + b.Property("Status") .HasColumnType("integer") .HasColumnName("status"); @@ -109,17 +152,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) { Id = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Email = "hans@müller.de", + FirstName = "Hans", + LastName = "Müller", Status = 0 }, new { Id = new Guid("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d"), ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), + Email = "friedrich@bäcker.at", + FirstName = "Friedrich", + LastName = "Bäcker", Status = 0 }); }); - modelBuilder.Entity("PlayOfferService.Models.PlayOffer", b => + modelBuilder.Entity("PlayOfferService.Domain.Models.PlayOffer", b => { b.Property("Id") .HasColumnType("uuid") @@ -165,21 +214,45 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("6fdd5f06-9099-4e7c-ad94-e184df54676b"), + Id = new Guid("a4e3d4e5-5215-4094-a333-ce3ee8154675"), ClubId = new Guid("06b812a7-5131-4510-82ff-bffac33e0f3e"), CreatorId = new Guid("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), IsCancelled = false, - ProposedEndTime = new DateTime(2024, 6, 2, 12, 39, 23, 865, DateTimeKind.Utc).AddTicks(7636), - ProposedStartTime = new DateTime(2024, 6, 2, 11, 39, 23, 865, DateTimeKind.Utc).AddTicks(7634) + ProposedEndTime = new DateTime(2024, 6, 22, 10, 27, 32, 806, DateTimeKind.Utc).AddTicks(9696), + ProposedStartTime = new DateTime(2024, 6, 22, 9, 27, 32, 806, DateTimeKind.Utc).AddTicks(9693) }); }); - modelBuilder.Entity("PlayOfferService.Models.Reservation", b => + modelBuilder.Entity("PlayOfferService.Domain.Models.Reservation", b => { b.Property("Id") .HasColumnType("uuid") .HasColumnName("id"); + b.Property("CourtId") + .HasColumnType("uuid") + .HasColumnName("courtId"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("endTime"); + + b.Property("IsCancelled") + .HasColumnType("boolean") + .HasColumnName("isCancelled"); + + b.Property>("ParticipantsIds") + .HasColumnType("uuid[]") + .HasColumnName("participantsIds"); + + b.Property("ReservantId") + .HasColumnType("uuid") + .HasColumnName("reservantId"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("startTime"); + b.HasKey("Id") .HasName("pK_reservations"); diff --git a/PlayOfferService.Tests/IntegrationTests/ClubEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/ClubEventHandlerTest.cs new file mode 100644 index 0000000..e9850e8 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/ClubEventHandlerTest.cs @@ -0,0 +1,196 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class ClubEventHandlerTest : TestSetup +{ + [SetUp] + public async Task ClubSetup() + { + var existingClubs = new List + { + new() + { + Id = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), + Name = "Test Club 1", + Status = Status.ACTIVE + }, + new() + { + Id = Guid.Parse("2db387f0-5792-4cb5-91a6-6317437b3432"), + Name = "Test Club 2", + Status = Status.ACTIVE + } + }; + + foreach (var club in existingClubs) + { + TestClubRepository.CreateClub(club); + } + await TestClubRepository.Update(); + + var existingPlayOffers = new List + { + new() + { + Id = Guid.Parse("d9afd6dd-8836-47dd-86be-3467a7db0fe5"), + ClubId = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), + CreatorId = Guid.Parse("4f5d3d99-1b8e-4276-8eff-32d484eef56c"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false + }, + new() + { + Id = Guid.Parse("9ee6d9d7-d4a6-4904-a20b-be026de53c4f"), + ClubId = Guid.Parse("2db387f0-5792-4cb5-91a6-6317437b3432"), + CreatorId = Guid.Parse("c18e24c1-21bf-4505-ae15-e1960c0f7a9b"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false + }, + }; + + foreach (var playOffer in existingPlayOffers) + { + TestPlayOfferRepository.CreatePlayOffer(playOffer); + } + + await TestPlayOfferRepository.Update(); + } + + [Test] + public async Task ClubCreatedEvent_ProjectionTest() + { + //Given + var clubId = Guid.NewGuid(); + var clubCreationEvent = new TechnicalClubEvent + { + EntityId = clubId, + EntityType = EntityType.TENNIS_CLUB, + EventId = Guid.NewGuid(), + EventType = EventType.TENNIS_CLUB_REGISTERED, + EventData = new ClubCreatedEvent + { + TennisClubId = new TennisClubId { Id = clubId }, + Name = "Test Club", + } + }; + + //When + await Mediator.Send(clubCreationEvent); + + //Then + var projectedClub = await TestClubRepository.GetClubById(clubId); + + Assert.That(projectedClub, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedClub!.Id, Is.EqualTo(clubId)); + Assert.That(projectedClub.Status, Is.EqualTo(Status.ACTIVE)); + }); + } + + [Test] + public async Task ClubLockedEvent_ProjectionTest() + { + //Given + var clubLockedEvent = new TechnicalClubEvent + { + EntityId = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), + EntityType = EntityType.TENNIS_CLUB, + EventId = Guid.Parse("ee90c3ee-3b4b-4ccb-8933-739795a7253a"), + EventType = EventType.TENNIS_CLUB_LOCKED, + EventData = new ClubLockedEvent() + }; + + //When + await Mediator.Send(clubLockedEvent); + + //Then + var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa")); + + Assert.That(projectedClub, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"))); + Assert.That(projectedClub.Status, Is.EqualTo(Status.LOCKED)); + }); + + var playOfferEvents = await TestWriteEventRepository.GetEventByEntityId(Guid.Parse("d9afd6dd-8836-47dd-86be-3467a7db0fe5")); + Assert.That(playOfferEvents, Has.Count.EqualTo(1)); + + Assert.Multiple(() => + { + Assert.That(playOfferEvents.First().EntityId, Is.EqualTo(Guid.Parse("d9afd6dd-8836-47dd-86be-3467a7db0fe5"))); + Assert.That(playOfferEvents.First().EventType, Is.EqualTo(EventType.PLAYOFFER_CANCELLED)); + Assert.That(playOfferEvents.First().CorrelationId, Is.EqualTo(Guid.Parse("ee90c3ee-3b4b-4ccb-8933-739795a7253a"))); + }); + } + + [Test] + public async Task ClubUnlockedEvent_ProjectionTest() + { + //Given + var clubUnlockedEvent = new TechnicalClubEvent + { + EntityId = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), + EntityType = EntityType.TENNIS_CLUB, + EventId = Guid.NewGuid(), + EventType = EventType.TENNIS_CLUB_UNLOCKED, + EventData = new ClubUnlockedEvent() + }; + + //When + await Mediator.Send(clubUnlockedEvent); + + //Then + var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa")); + + Assert.That(projectedClub, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"))); + Assert.That(projectedClub.Status, Is.EqualTo(Status.ACTIVE)); + }); + } + + [Test] + public async Task ClubDeletedEvent_ProjectionTest() + { + //Given + var clubDeletedEvent = new TechnicalClubEvent + { + EntityId = Guid.Parse("2db387f0-5792-4cb5-91a6-6317437b3432"), + EntityType = EntityType.TENNIS_CLUB, + EventId = Guid.Parse("ab0ecc19-e492-4126-aac5-5448198d62cc"), + EventType = EventType.TENNIS_CLUB_DELETED, + EventData = new ClubDeletedEvent() + }; + + //When + await Mediator.Send(clubDeletedEvent); + + //Then + var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("2db387f0-5792-4cb5-91a6-6317437b3432")); + + Assert.That(projectedClub, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("2db387f0-5792-4cb5-91a6-6317437b3432"))); + Assert.That(projectedClub.Status, Is.EqualTo(Status.DELETED)); + }); + + var playOfferEvents = await TestWriteEventRepository.GetEventByEntityId(Guid.Parse("9ee6d9d7-d4a6-4904-a20b-be026de53c4f")); + Assert.That(playOfferEvents, Has.Count.EqualTo(1)); + + Assert.Multiple(() => + { + Assert.That(playOfferEvents.First().EntityId, Is.EqualTo(Guid.Parse("9ee6d9d7-d4a6-4904-a20b-be026de53c4f"))); + Assert.That(playOfferEvents.First().EventType, Is.EqualTo(EventType.PLAYOFFER_CANCELLED)); + Assert.That(playOfferEvents.First().CorrelationId, Is.EqualTo(Guid.Parse("ab0ecc19-e492-4126-aac5-5448198d62cc"))); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/ClubRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/ClubRepositoryTest.cs index 60e12aa..a636bae 100644 --- a/PlayOfferService.Tests/IntegrationTests/ClubRepositoryTest.cs +++ b/PlayOfferService.Tests/IntegrationTests/ClubRepositoryTest.cs @@ -1,7 +1,4 @@ -using PlayOfferService.Domain.Events; -using PlayOfferService.Domain.Events.Member; using PlayOfferService.Domain.Models; -using PlayOfferService.Domain.ValueObjects; namespace PlayOfferService.Tests.IntegrationTests; @@ -11,151 +8,66 @@ public class ClubRepositoryTest : TestSetup [SetUp] public async Task ClubSetup() { - var clubCreationEvent = new BaseEvent + var existingClub = new Club { - EntityId = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_REGISTERED, - EventData = new ClubCreatedEvent - { - TennisClubId = new TennisClubId {Id = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa")} - } + Id = Guid.Parse("67bc285f-1b28-40f9-8b9e-564b3d9c1297"), + Name = "Test Club", + Status = Status.ACTIVE }; - - var lockedClubCreationEvent = new BaseEvent - { - EntityId = Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_REGISTERED, - EventData = new ClubCreatedEvent - { - TennisClubId = new TennisClubId{Id=Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac")} - } - }; - var clubLockedEvent = new BaseEvent - { - EntityId = Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_LOCKED, - EventData = new ClubLockedEvent() - }; - await TestClubRepository.UpdateEntityAsync(clubCreationEvent); - await TestClubRepository.UpdateEntityAsync(lockedClubCreationEvent); - await TestClubRepository.UpdateEntityAsync(clubLockedEvent); + TestClubRepository.CreateClub(existingClub); + await TestClubRepository.Update(); } - - [Test] - public async Task ClubCreatedEvent_ProjectionTest() - { - //Given - var clubId = Guid.NewGuid(); - var clubCreationEvent = new BaseEvent - { - EntityId = clubId, - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_REGISTERED, - EventData = new ClubCreatedEvent - { - TennisClubId = new TennisClubId{Id=clubId} - } - }; - - //When - await TestClubRepository.UpdateEntityAsync(clubCreationEvent); - - //Then - var projectedClub = await TestClubRepository.GetClubById(clubId); - - Assert.That(projectedClub, Is.Not.Null); - Assert.Multiple(() => - { - Assert.That(projectedClub!.Id, Is.EqualTo(clubId)); - Assert.That(projectedClub.Status, Is.EqualTo(Status.ACTIVE)); - }); - } - + [Test] - public async Task ClubLockedEvent_ProjectionTest() + public async Task GetExistingClubByIdTest() { //Given - var clubLockedEvent = new BaseEvent - { - EntityId = Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_LOCKED, - EventData = new ClubLockedEvent() - }; - + var clubId = Guid.Parse("67bc285f-1b28-40f9-8b9e-564b3d9c1297"); + //When - await TestClubRepository.UpdateEntityAsync(clubLockedEvent); - + var club = await TestClubRepository.GetClubById(clubId); + //Then - var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa")); - - Assert.That(projectedClub, Is.Not.Null); - Assert.Multiple(() => - { - Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("8aa54411-32fe-4b4c-a017-aa9710cb3bfa"))); - Assert.That(projectedClub.Status, Is.EqualTo(Status.LOCKED)); - }); + Assert.That(club, Is.Not.Null); + Assert.That(club!.Id, Is.EqualTo(clubId)); } [Test] - public async Task ClubUnlockedEvent_ProjectionTest() + public async Task GetNonExistingClubByIdTest() { //Given - var clubUnlockedEvent = new BaseEvent - { - EntityId = Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_UNLOCKED, - EventData = new ClubUnlockedEvent() - }; + var clubId = Guid.NewGuid(); //When - await TestClubRepository.UpdateEntityAsync(clubUnlockedEvent); + var club = await TestClubRepository.GetClubById(clubId); //Then - var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac")); - - Assert.That(projectedClub, Is.Not.Null); - Assert.Multiple(() => - { - Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"))); - Assert.That(projectedClub.Status, Is.EqualTo(Status.ACTIVE)); - }); + Assert.That(club, Is.Null); } - + [Test] - public async Task ClubDeletedEvent_ProjectionTest() + public async Task CreateClubTest() { //Given - var clubDeletedEvent = new BaseEvent + var clubId = Guid.NewGuid(); + var newClub = new Club { - EntityId = Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"), - EntityType = EntityType.TENNIS_CLUB, - EventId = Guid.NewGuid(), - EventType = EventType.TENNIS_CLUB_DELETED, - EventData = new ClubDeletedEvent() + Id = clubId, + Name = "Test Club", + Status = Status.ACTIVE }; //When - await TestClubRepository.UpdateEntityAsync(clubDeletedEvent); + TestClubRepository.CreateClub(newClub); + await TestClubRepository.Update(); //Then - var projectedClub = await TestClubRepository.GetClubById(Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac")); - - Assert.That(projectedClub, Is.Not.Null); + var club = await TestClubRepository.GetClubById(clubId); + Assert.That(club, Is.Not.Null); Assert.Multiple(() => { - Assert.That(projectedClub!.Id, Is.EqualTo(Guid.Parse("9ad0861f-89d0-40f2-899c-58525d381aac"))); - Assert.That(projectedClub.Status, Is.EqualTo(Status.DELETED)); + Assert.That(club!.Id, Is.EqualTo(clubId)); + Assert.That(club.Status, Is.EqualTo(Status.ACTIVE)); }); } } \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/CourtEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/CourtEventHandlerTest.cs new file mode 100644 index 0000000..c2ec3df --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/CourtEventHandlerTest.cs @@ -0,0 +1,74 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Court; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class CourtEventHandlerTest : TestSetup +{ + [SetUp] + public async Task CourtSetup() + { + var existingCourt = new Court { + Id = Guid.Parse("3e47acd8-55d6-4042-8fc2-69c3ee9fae31"), + ClubId = Guid.Parse("588cf6e7-04a8-4de4-99d0-5ec2b0daa19a"), + Name = "TestCourt 10" + }; + TestCourtRepository.CreateCourt(existingCourt); + await TestReservationRepository.Update(); + } + + [Test] + public async Task CourtCreatedEvent_ProjectionTest() + { + // Given + var courtCreatedEvent = new TechnicalCourtEvent { + EventId = Guid.Parse("fac8ccd9-b523-4cff-bcb6-db63bdf9207f"), + EventType = EventType.CourtCreatedEvent, + EntityId = Guid.Parse("d2c39afd-5e1d-44c0-b339-c0b53e8eee2d"), + EventData = new CourtCreatedEvent { + ClubId = Guid.Parse("6e20d048-836b-48d0-8031-36a40353bc6a"), + Name = "TestCourt 20" + } + }; + + // When + await Mediator.Send(courtCreatedEvent); + + // Then + var projectedCourt = await TestCourtRepository.GetCourtById(courtCreatedEvent.EntityId); + + Assert.That(projectedCourt, Is.Not.Null); + Assert.Multiple(() => { + Assert.That(projectedCourt!.Id, Is.EqualTo(Guid.Parse("d2c39afd-5e1d-44c0-b339-c0b53e8eee2d"))); + Assert.That(projectedCourt.ClubId, Is.EqualTo(Guid.Parse("6e20d048-836b-48d0-8031-36a40353bc6a"))); + Assert.That(projectedCourt.Name, Is.EqualTo("TestCourt 20")); + }); + } + + [Test] + public async Task CourtUpdatedEvent_ProjectionTest() + { + // Given + var courtUpdatedEvent = new TechnicalCourtEvent { + EventId = Guid.Parse("2cc26a0f-b75c-4d1f-8098-8dcb02cb3685"), + EventType = EventType.CourtUpdatedEvent, + EntityId = Guid.Parse("3e47acd8-55d6-4042-8fc2-69c3ee9fae31"), + EventData = new CourtUpdatedEvent { + CourtName = "TestCourt 20 - Updated" + } + }; + + // When + await Mediator.Send(courtUpdatedEvent); + + // Then + var projectedCourt = await TestCourtRepository.GetCourtById(courtUpdatedEvent.EntityId); + + Assert.That(projectedCourt, Is.Not.Null); + Assert.Multiple(() => { + Assert.That(projectedCourt!.Id, Is.EqualTo(Guid.Parse("3e47acd8-55d6-4042-8fc2-69c3ee9fae31"))); + Assert.That(projectedCourt.Name, Is.EqualTo("TestCourt 20 - Updated")); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/CourtRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/CourtRepositoryTest.cs new file mode 100644 index 0000000..ebfe9d3 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/CourtRepositoryTest.cs @@ -0,0 +1,78 @@ +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class CourtRepositoryTest : TestSetup +{ + [SetUp] + public async Task ReservationSetup() + { + var existingCourt = new Court + { + Id = Guid.Parse("156a5115-98ef-4e4e-a9c2-b1c4beac4e92"), + ClubId = Guid.Parse("8b5a51c8-5945-446f-8be6-6372eaacc542"), + Name = "Court 42" + }; + TestCourtRepository.CreateCourt(existingCourt); + await TestReservationRepository.Update(); + } + + [Test] + public async Task GetExistingReservationByIdTest() + { + //Given + var courtId = Guid.Parse("156a5115-98ef-4e4e-a9c2-b1c4beac4e92"); + + //When + var court = await TestCourtRepository.GetCourtById(courtId); + + //Then + Assert.That(court, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(court!.Id, Is.EqualTo(courtId)); + Assert.That(court.ClubId, Is.EqualTo(Guid.Parse("8b5a51c8-5945-446f-8be6-6372eaacc542"))); + Assert.That(court.Name, Is.EqualTo("Court 42")); + }); + } + + [Test] + public async Task GetNonExistingReservationByIdTest() + { + //Given + var courtId = Guid.NewGuid(); + + //When + var court = await TestCourtRepository.GetCourtById(courtId); + + //Then + Assert.That(court, Is.Null); + } + + [Test] + public async Task CreateReservationTest() + { + //Given + var givenCourt = new Court + { + Id = Guid.Parse("4a857292-7957-4731-8b89-9d8899613139"), + ClubId = Guid.Parse("a534cb67-3254-40f6-a661-316788e7a5f1"), + Name = "Court 1" + }; + + //When + TestCourtRepository.CreateCourt(givenCourt); + await TestCourtRepository.Update(); + + //Then + var court = await TestCourtRepository.GetCourtById(givenCourt.Id); + + Assert.That(court, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(court!.Id, Is.EqualTo(Guid.Parse("4a857292-7957-4731-8b89-9d8899613139"))); + Assert.That(court.ClubId, Is.EqualTo(Guid.Parse("a534cb67-3254-40f6-a661-316788e7a5f1"))); + Assert.That(court.Name, Is.EqualTo("Court 1")); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/MemberEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/MemberEventHandlerTest.cs new file mode 100644 index 0000000..3cfaa39 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/MemberEventHandlerTest.cs @@ -0,0 +1,207 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Member; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class MemberEventHandlerTest : TestSetup +{ + [SetUp] + public async Task MemberSetup() + { + var existingMembers = new List + { + new() + { + Id = Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b"), + ClubId = Guid.Parse("bf7f59db-e2bf-4a4f-95fe-baeabe948b81"), + Status = Status.ACTIVE, + FirstName = "Sebastian", + LastName = "Schmidt", + Email = "seb@stian.com" + }, + new() + { + Id = Guid.Parse("c559d8ad-67be-4739-afb0-94460d7bb100"), + ClubId = Guid.Parse("8ad9ddaa-8ce6-4655-b2da-fb3419f93a67"), + Status = Status.ACTIVE, + FirstName = "Lisa", + LastName = "Müller", + Email = "lisa@müller.com" + } + }; + + foreach (var member in existingMembers) + { + TestMemberRepository.CreateMember(member); + } + await TestMemberRepository.Update(); + + var existingPlayOffers = new List + { + new() + { + Id = Guid.Parse("033cad0e-6270-487c-85e2-02a7b531b1cd"), + ClubId = Guid.Parse("c7545dea-2b37-4ee9-b99b-7ee422565870"), + CreatorId = Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false + }, + new() + { + Id = Guid.Parse("301289c5-6789-4a64-b9f6-37a141191123"), + ClubId = Guid.Parse("7b219549-58fb-4018-8958-151eacab2d1c"), + CreatorId = Guid.Parse("c559d8ad-67be-4739-afb0-94460d7bb100"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false + }, + }; + + foreach (var playOffer in existingPlayOffers) + { + TestPlayOfferRepository.CreatePlayOffer(playOffer); + } + + await TestPlayOfferRepository.Update(); + } + + [Test] + public async Task MemberCreatedEvent_ProjectionTest() + { + //Given + var memberId = Guid.NewGuid(); + var memberCreatedEvent = new TechnicalMemberEvent + { + EntityId = memberId, + EntityType = EntityType.MEMBER, + EventId = Guid.NewGuid(), + EventType = EventType.MEMBER_REGISTERED, + EventData = new MemberCreatedEvent + { + MemberId = new MemberId { Id = memberId }, + Name = new FullName { FirstName = "Test", LastName = "Member" }, + TennisClubId = new TennisClubId { Id = Guid.Parse("bf7f59db-e2bf-4a4f-95fe-baeabe948b81") }, + Email = "test@member.de", + } + }; + + //When + await Mediator.Send(memberCreatedEvent); + + //Then + var projectedMember = await TestMemberRepository.GetMemberById(memberId); + + Assert.That(projectedMember, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedMember!.Id, Is.EqualTo(memberId)); + Assert.That(projectedMember.ClubId, Is.EqualTo(Guid.Parse("bf7f59db-e2bf-4a4f-95fe-baeabe948b81"))); + Assert.That(projectedMember.Status, Is.EqualTo(Status.ACTIVE)); + }); + } + + [Test] + public async Task MemberLockEvent_ProjectionTest() + { + //Given + var memberLockEvent = new TechnicalMemberEvent + { + EntityId = Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b"), + EntityType = EntityType.MEMBER, + EventId = Guid.Parse("d1f2c32e-77b3-4e3b-a632-7ea710e93b44"), + EventType = EventType.MEMBER_LOCKED, + EventData = new MemberLockedEvent() + }; + + //When + await Mediator.Send(memberLockEvent); + + //Then + var projectedMember = await TestMemberRepository.GetMemberById(Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b")); + + Assert.That(projectedMember, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedMember!.Id, Is.EqualTo(Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b"))); + Assert.That(projectedMember.Status, Is.EqualTo(Status.LOCKED)); + }); + + var playOfferEvents = await TestWriteEventRepository.GetEventByEntityId(Guid.Parse("033cad0e-6270-487c-85e2-02a7b531b1cd")); + Assert.That(playOfferEvents, Has.Count.EqualTo(1)); + + Assert.Multiple(() => + { + Assert.That(playOfferEvents.First().EntityId, Is.EqualTo(Guid.Parse("033cad0e-6270-487c-85e2-02a7b531b1cd"))); + Assert.That(playOfferEvents.First().EventType, Is.EqualTo(EventType.PLAYOFFER_CANCELLED)); + Assert.That(playOfferEvents.First().CorrelationId, Is.EqualTo(Guid.Parse("d1f2c32e-77b3-4e3b-a632-7ea710e93b44"))); + }); + } + + [Test] + public async Task MemberUnlockEvent_ProjectionTest() + { + //Given + var existingMember = await TestMemberRepository.GetMemberById(Guid.Parse("971c48ff-42f4-4dc5-94ad-42e3c155b07b")); + var memberUnlockEvent = new TechnicalMemberEvent + { + EntityId = existingMember!.Id, + EntityType = EntityType.MEMBER, + EventId = Guid.NewGuid(), + EventType = EventType.MEMBER_UNLOCKED, + EventData = new MemberUnlockedEvent() + }; + + //When + await Mediator.Send(memberUnlockEvent); + + //Then + var projectedMember = await TestMemberRepository.GetMemberById(existingMember.Id); + + Assert.That(projectedMember, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedMember!.Id, Is.EqualTo(existingMember.Id)); + Assert.That(projectedMember.Status, Is.EqualTo(Status.ACTIVE)); + }); + } + + [Test] + public async Task MemberDeletedEvent_ProjectionTest() + { + //Given + var memberDeleteEvent = new TechnicalMemberEvent + { + EntityId = Guid.Parse("c559d8ad-67be-4739-afb0-94460d7bb100"), + EntityType = EntityType.MEMBER, + EventId = Guid.Parse("367d90df-ef2e-44cf-a6e1-704fbafbb561"), + EventType = EventType.MEMBER_DELETED, + EventData = new MemberDeletedEvent() + }; + + //When + await Mediator.Send(memberDeleteEvent); + + //Then + var projectedMember = await TestMemberRepository.GetMemberById(Guid.Parse("c559d8ad-67be-4739-afb0-94460d7bb100")); + + Assert.That(projectedMember, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedMember!.Id, Is.EqualTo(Guid.Parse("c559d8ad-67be-4739-afb0-94460d7bb100"))); + Assert.That(projectedMember.Status, Is.EqualTo(Status.DELETED)); + }); + + var playOfferEvents = await TestWriteEventRepository.GetEventByEntityId(Guid.Parse("301289c5-6789-4a64-b9f6-37a141191123")); + Assert.That(playOfferEvents, Has.Count.EqualTo(1)); + + Assert.Multiple(() => + { + Assert.That(playOfferEvents.First().EntityId, Is.EqualTo(Guid.Parse("301289c5-6789-4a64-b9f6-37a141191123"))); + Assert.That(playOfferEvents.First().EventType, Is.EqualTo(EventType.PLAYOFFER_CANCELLED)); + Assert.That(playOfferEvents.First().CorrelationId, Is.EqualTo(Guid.Parse("367d90df-ef2e-44cf-a6e1-704fbafbb561"))); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/MemberRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/MemberRepositoryTest.cs index fc26d14..94bc80a 100644 --- a/PlayOfferService.Tests/IntegrationTests/MemberRepositoryTest.cs +++ b/PlayOfferService.Tests/IntegrationTests/MemberRepositoryTest.cs @@ -1,182 +1,138 @@ -using NSubstitute; -using PlayOfferService.Domain.Events; -using PlayOfferService.Domain.Events.Member; using PlayOfferService.Domain.Models; -using PlayOfferService.Domain.Repositories; -using PlayOfferService.Domain.ValueObjects; namespace PlayOfferService.Tests.IntegrationTests; [TestFixture] public class MemberRepositoryTest : TestSetup { - private ClubRepository _clubRepositoryMock = Substitute.For(); - [SetUp] public async Task MemberSetup() { - var testClub = new Club { Id = Guid.NewGuid(), Status = Status.ACTIVE }; - _clubRepositoryMock = Substitute.For(); - _clubRepositoryMock.GetClubById(Guid.NewGuid()).ReturnsForAnyArgs(testClub); - - var memberCreationEvent = new BaseEvent + var existingMember = new Member { - EntityId = Guid.Parse("16a1a213-f684-4e08-b9ef-61b372c59bf4"), - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_REGISTERED, - EventData = new MemberCreatedEvent - { - MemberId = new MemberId{Id=Guid.Parse("16a1a213-f684-4e08-b9ef-61b372c59bf4")}, - TennisClubId = new TennisClubId{Id=testClub.Id} - } + Id = Guid.Parse("8bbb752a-784c-4fb1-9484-0522b4fb78d9"), + ClubId = Guid.Parse("b0dd93aa-4b7d-4d36-89ed-be056976ca84"), + FirstName = "Max", + LastName = "Mustermann", + Email = "maxi@musti.at", + Status = Status.ACTIVE }; - await TestMemberRepository.UpdateEntityAsync(memberCreationEvent); + TestMemberRepository.CreateMember(existingMember); + await TestMemberRepository.Update(); + } + + [Test] + public async Task GetExistingMemberByIdTest() + { + //Given + var memberId = Guid.Parse("8bbb752a-784c-4fb1-9484-0522b4fb78d9"); - var lockedMemberCreationEvent = new BaseEvent - { - EntityId = Guid.Parse("d920f6c9-e328-4e84-be64-0a586269f89d"), - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_REGISTERED, - EventData = new MemberCreatedEvent - { - MemberId = new MemberId{Id=Guid.Parse("d920f6c9-e328-4e84-be64-0a586269f89d")}, - TennisClubId = new TennisClubId{Id=testClub.Id} - } - }; - var memberLockEvent = new BaseEvent - { - EntityId = Guid.Parse("d920f6c9-e328-4e84-be64-0a586269f89d"), - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_LOCKED, - EventData = new MemberLockedEvent() - }; - await TestMemberRepository.UpdateEntityAsync(lockedMemberCreationEvent); - await TestMemberRepository.UpdateEntityAsync(memberLockEvent); + //When + var member = await TestMemberRepository.GetMemberById(memberId); + //Then + Assert.That(member, Is.Not.Null); + Assert.That(member!.Id, Is.EqualTo(memberId)); } [Test] - public async Task MemberCreatedEvent_ProjectionTest() + public async Task GetNonExistingMemberByIdTest() { //Given - var testClub = await _clubRepositoryMock.GetClubById(Guid.NewGuid()); + var memberId = Guid.NewGuid(); + + //When + var member = await TestMemberRepository.GetMemberById(memberId); + //Then + Assert.That(member, Is.Null); + } + + [Test] + public async Task CreateMemberTest() + { + //Given var memberId = Guid.NewGuid(); - var memberCreatedEvent = new BaseEvent + var newMember = new Member { - EntityId = memberId, - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_REGISTERED, - EventData = new MemberCreatedEvent - { - MemberId = new MemberId{Id=memberId}, - TennisClubId = new TennisClubId{Id=testClub!.Id} - } + Id = memberId, + ClubId = Guid.Parse("b0dd93aa-4b7d-4d36-89ed-be056976ca84"), + Status = Status.ACTIVE, + FirstName = "David", + LastName = "Doe", + Email = "david@doe.com" }; //When - await TestMemberRepository.UpdateEntityAsync(memberCreatedEvent); + TestMemberRepository.CreateMember(newMember); + await TestMemberRepository.Update(); //Then - var projectedMember = await TestMemberRepository.GetMemberById(memberId); + var member = await TestMemberRepository.GetMemberById(memberId); - Assert.That(projectedMember, Is.Not.Null); + Assert.That(member, Is.Not.Null); Assert.Multiple(() => { - if (projectedMember != null) - { - Assert.That(projectedMember.Id, Is.EqualTo(memberId)); - Assert.That(projectedMember.ClubId, Is.EqualTo(testClub.Id)); - Assert.That(projectedMember.Status, Is.EqualTo(Status.ACTIVE)); - } + Assert.That(member!.Id, Is.EqualTo(memberId)); + Assert.That(member.ClubId, Is.EqualTo(Guid.Parse("b0dd93aa-4b7d-4d36-89ed-be056976ca84"))); + Assert.That(member.Status, Is.EqualTo(Status.ACTIVE)); }); } [Test] - public async Task MemberLockEvent_ProjectionTest() + public async Task GetExistingMemberByFullNameTest() { //Given - var existingMember = await TestMemberRepository.GetMemberById(Guid.Parse("16a1a213-f684-4e08-b9ef-61b372c59bf4")); - var memberLockEvent = new BaseEvent - { - EntityId = existingMember!.Id, - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_LOCKED, - EventData = new MemberLockedEvent() - }; + var creatorName = "Max Mustermann"; //When - await TestMemberRepository.UpdateEntityAsync(memberLockEvent); + var members = await TestMemberRepository.GetMemberByName(creatorName); //Then - var projectedMember = await TestMemberRepository.GetMemberById(existingMember.Id); - - Assert.That(projectedMember, Is.Not.Null); + Assert.That(members, Is.Not.Null); + Assert.That(members.Count, Is.EqualTo(1)); Assert.Multiple(() => { - Assert.That(projectedMember!.Id, Is.EqualTo(existingMember.Id)); - Assert.That(projectedMember.Status, Is.EqualTo(Status.LOCKED)); + Assert.That(members[0].FirstName, Is.EqualTo("Max")); + Assert.That(members[0].LastName, Is.EqualTo("Mustermann")); }); } [Test] - public async Task MemberUnlockEvent_ProjectionTest() + public async Task GetExistingMemberByFirstNameTest() { //Given - var existingMember = await TestMemberRepository.GetMemberById(Guid.Parse("d920f6c9-e328-4e84-be64-0a586269f89d")); - var memberUnlockEvent = new BaseEvent - { - EntityId = existingMember!.Id, - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_UNLOCKED, - EventData = new MemberUnlockedEvent() - }; + var creatorName = "ax"; //When - await TestMemberRepository.UpdateEntityAsync(memberUnlockEvent); + var members = await TestMemberRepository.GetMemberByName(creatorName); //Then - var projectedMember = await TestMemberRepository.GetMemberById(existingMember.Id); - - Assert.That(projectedMember, Is.Not.Null); + Assert.That(members, Is.Not.Null); + Assert.That(members.Count, Is.EqualTo(1)); Assert.Multiple(() => { - Assert.That(projectedMember!.Id, Is.EqualTo(existingMember.Id)); - Assert.That(projectedMember.Status, Is.EqualTo(Status.ACTIVE)); + Assert.That(members[0].FirstName, Is.EqualTo("Max")); + Assert.That(members[0].LastName, Is.EqualTo("Mustermann")); }); } [Test] - public async Task MemberDeletedEvent_ProjectionTest() + public async Task GetExistingMemberByLastNameTest() { //Given - var existingMember = await TestMemberRepository.GetMemberById(Guid.Parse("d920f6c9-e328-4e84-be64-0a586269f89d")); - var memberDeleteEvent = new BaseEvent - { - EntityId = existingMember!.Id, - EntityType = EntityType.MEMBER, - EventId = Guid.NewGuid(), - EventType = EventType.MEMBER_DELETED, - EventData = new MemberDeletedEvent() - }; + var creatorName = "ustermann"; //When - await TestMemberRepository.UpdateEntityAsync(memberDeleteEvent); + var members = await TestMemberRepository.GetMemberByName(creatorName); //Then - var projectedMember = await TestMemberRepository.GetMemberById(existingMember.Id); - - Assert.That(projectedMember, Is.Not.Null); + Assert.That(members, Is.Not.Null); + Assert.That(members.Count, Is.EqualTo(1)); Assert.Multiple(() => { - Assert.That(projectedMember!.Id, Is.EqualTo(existingMember.Id)); - Assert.That(projectedMember.Status, Is.EqualTo(Status.DELETED)); + Assert.That(members[0].FirstName, Is.EqualTo("Max")); + Assert.That(members[0].LastName, Is.EqualTo("Mustermann")); }); } } \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/PlayOfferEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/PlayOfferEventHandlerTest.cs new file mode 100644 index 0000000..d7389e2 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/PlayOfferEventHandlerTest.cs @@ -0,0 +1,195 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class PlayOfferEventHandlerTest : TestSetup +{ + [SetUp] + public async Task PlayOfferSetup() + { + var existingPlayOffers = new List{ + new() + { + Id = Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"), + ClubId = Guid.Parse("6031061f-86c6-459b-9260-5b4772bae9d3"), + CreatorId = Guid.Parse("dd365b58-fa17-4df5-8d0c-a67f762f70f3"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false + }, + new() + { + Id = Guid.Parse("fd385a56-17b1-4318-bc46-cf7f75f20283"), + ClubId = Guid.Parse("6031061f-86c6-459b-9260-5b4772bae9d3"), + CreatorId = Guid.Parse("dd365b58-fa17-4df5-8d0c-a67f762f70f3"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + IsCancelled = false, + OpponentId = Guid.Parse("c4d9309e-a28a-4065-a213-69285ed7da5c"), + AcceptedStartTime = DateTime.UtcNow.AddHours(1) + }, + }; + + foreach (var playOffer in existingPlayOffers) + { + TestPlayOfferRepository.CreatePlayOffer(playOffer); + } + await TestPlayOfferRepository.Update(); + } + + [Test] + public async Task PlayOfferCreatedEvent_ProjectionTest() + { + //Given + var playOfferId = Guid.NewGuid(); + var givenStartTime = DateTime.UtcNow; + var givenEndTime = DateTime.UtcNow.AddHours(3); + var playOfferEvent = new TechnicalPlayOfferEvent + { + EntityId = playOfferId, + EntityType = EntityType.PLAYOFFER, + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_CREATED, + EventData = new PlayOfferCreatedEvent + { + Id = playOfferId, + ClubId = Guid.Parse("437555ba-cc03-4adf-b8d8-809daccc1006"), + CreatorId = Guid.Parse("f2d11d00-305e-492a-adc0-0e8e63328d66"), + ProposedStartTime = givenStartTime, + ProposedEndTime = givenEndTime + } + }; + + //When + await Mediator.Send(playOfferEvent); + + //Then + var projectedPlayOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(playOfferId)).ToList(); + + Assert.That(projectedPlayOffers, Has.Count.EqualTo(1)); + Assert.Multiple(() => + { + Assert.That(projectedPlayOffers[0].Id, Is.EqualTo(playOfferId)); + Assert.That(projectedPlayOffers[0].ClubId, Is.EqualTo(Guid.Parse("437555ba-cc03-4adf-b8d8-809daccc1006"))); + Assert.That(projectedPlayOffers[0].CreatorId, Is.EqualTo(Guid.Parse("f2d11d00-305e-492a-adc0-0e8e63328d66"))); + Assert.That(projectedPlayOffers[0].ProposedStartTime, Is.EqualTo(givenStartTime)); + Assert.That(projectedPlayOffers[0].ProposedEndTime, Is.EqualTo(givenEndTime)); + Assert.That(projectedPlayOffers[0].IsCancelled, Is.False); + }); + } + + [Test] + public async Task PlayOfferCancelledEvent_ProjectionTest() + { + //Given + var existingPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + var playOfferCancelledEvent = new TechnicalPlayOfferEvent + { + EntityId = existingPlayOffer.Id, + EntityType = EntityType.PLAYOFFER, + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_CANCELLED, + EventData = new PlayOfferCancelledEvent() + }; + + //When + await Mediator.Send(playOfferCancelledEvent); + + //Then + var projectedPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + + Assert.That(projectedPlayOffer, Is.Not.Null); + Assert.That(projectedPlayOffer.IsCancelled, Is.True); + } + + [Test] + public async Task PlayOfferJoinedEvent_ProjectionTest() + { + //Given + var existingPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + var givenAcceptedTime = DateTime.UtcNow.AddHours(1); + var playOfferJoinedEvent = new TechnicalPlayOfferEvent + { + EntityId = existingPlayOffer.Id, + EntityType = EntityType.PLAYOFFER, + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_JOINED, + EventData = new PlayOfferJoinedEvent + { + OpponentId = Guid.Parse("f2d11d00-305e-492a-adc0-0e8e63328d66"), + AcceptedStartTime = givenAcceptedTime + } + }; + + //When + await Mediator.Send(playOfferJoinedEvent); + + //Then + var projectedPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + + Assert.That(projectedPlayOffer, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedPlayOffer.AcceptedStartTime, Is.EqualTo(givenAcceptedTime)); + Assert.That(projectedPlayOffer.OpponentId, Is.EqualTo(Guid.Parse("f2d11d00-305e-492a-adc0-0e8e63328d66"))); + }); + } + + [Test] + public async Task PlayOfferOpponentRemovedEvent_ProjectionTest() + { + //Given + var existingPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("fd385a56-17b1-4318-bc46-cf7f75f20283"))).First(); + var playOfferOpponentRemovedEvent = new TechnicalPlayOfferEvent + { + EntityId = existingPlayOffer.Id, + EntityType = EntityType.PLAYOFFER, + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_OPPONENT_REMOVED, + EventData = new PlayOfferOpponentRemovedEvent() + }; + + //When + await Mediator.Send(playOfferOpponentRemovedEvent); + + //Then + var projectedPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("fd385a56-17b1-4318-bc46-cf7f75f20283"))).First(); + + Assert.That(projectedPlayOffer, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedPlayOffer.OpponentId, Is.Null); + Assert.That(projectedPlayOffer.AcceptedStartTime, Is.Null); + }); + } + + [Test] + public async Task PlayOfferReservationAddedEvent_ProjectionTest() + { + //Given + var existingPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + var givenReservationId = Guid.NewGuid(); + var playOfferReservationAddedEvent = new TechnicalPlayOfferEvent + { + EntityId = existingPlayOffer.Id, + EntityType = EntityType.PLAYOFFER, + EventId = Guid.NewGuid(), + EventType = EventType.PLAYOFFER_RESERVATION_ADDED, + EventData = new PlayOfferReservationAddedEvent + { + ReservationId = givenReservationId + } + }; + + //When + await Mediator.Send(playOfferReservationAddedEvent); + + //Then + var projectedPlayOffer = (await TestPlayOfferRepository.GetPlayOffersByIds(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"))).First(); + + Assert.That(projectedPlayOffer, Is.Not.Null); + Assert.That(projectedPlayOffer.ReservationId, Is.EqualTo(givenReservationId)); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/PlayOfferRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/PlayOfferRepositoryTest.cs new file mode 100644 index 0000000..e915a15 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/PlayOfferRepositoryTest.cs @@ -0,0 +1,159 @@ +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class PlayOfferRepositoryTest : TestSetup +{ + [SetUp] + public async Task PlayOfferSetup() + { + var existingPlayOffers = new List { + new() + { + Id = Guid.Parse("d79f0bd6-c7ec-44e5-a02f-26e1567b0992"), + ClubId = Guid.Parse("34f13619-14b5-4244-a74b-6a8ba210a0b1"), + CreatorId = Guid.Parse("9b30e631-3ad8-4437-a934-94252d6294c4"), + ReservationId = Guid.Parse("b5fab7b7-63f1-4847-a71c-ae37dbfed029") + }, + new() + { + Id = Guid.Parse("9b0e6c95-1a00-49a0-9414-f83ed0eb388f"), + ClubId = Guid.Parse("34f13619-14b5-4244-a74b-6a8ba210a0b1"), + CreatorId = Guid.Parse("4fe95e2d-a943-444e-99aa-396d141b03ec"), + }, + new() + { + Id = Guid.Parse("d71ad67e-fa99-4ef2-b3ee-c6be640607a8"), + ClubId = Guid.Parse("34f13619-14b5-4244-a74b-6a8ba210a0b1"), + CreatorId = Guid.Parse("9b30e631-3ad8-4437-a934-94252d6294c4"), + OpponentId = Guid.Parse("4fe95e2d-a943-444e-99aa-396d141b03ec"), + } + }; + foreach (var playOffer in existingPlayOffers) + { + TestPlayOfferRepository.CreatePlayOffer(playOffer); + } + await TestPlayOfferRepository.Update(); + } + + [Test] + public async Task GetExistingPlayOfferByIdTest() + { + //Given + var playOfferId = Guid.Parse("d79f0bd6-c7ec-44e5-a02f-26e1567b0992"); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(playOfferId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(1)); + Assert.That(playOffers.First().Id, Is.EqualTo(playOfferId)); + } + + [Test] + public async Task GetNonExistingPlayOfferByIdTest() + { + //Given + var playOfferId = Guid.NewGuid(); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(playOfferId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Is.Empty); + } + + [Test] + public async Task GetPlayOffersByClubIdTest() + { + //Given + var clubId = Guid.Parse("34f13619-14b5-4244-a74b-6a8ba210a0b1"); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(null,null,clubId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(3)); + } + + [Test] + public async Task GetPlayOffersByCreatorIdTest() + { + //Given + var creatorId = Guid.Parse("9b30e631-3ad8-4437-a934-94252d6294c4"); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(null,creatorId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(2)); + Assert.Multiple(() => + { + Assert.That(playOffers[1].Id, Is.EqualTo(Guid.Parse("d79f0bd6-c7ec-44e5-a02f-26e1567b0992"))); + Assert.That(playOffers[0].Id, Is.EqualTo(Guid.Parse("d71ad67e-fa99-4ef2-b3ee-c6be640607a8"))); + }); + } + + [Test] + + public async Task GetPlayOffersByOpponentIdTest() + { + //Given + var opponentId = Guid.Parse("4fe95e2d-a943-444e-99aa-396d141b03ec"); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(null,null,null,opponentId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(1)); + Assert.That(playOffers.First().Id, Is.EqualTo(Guid.Parse("d71ad67e-fa99-4ef2-b3ee-c6be640607a8"))); + } + + [Test] + public async Task GetPlayOffersByParticipantId() + { + //Given + var participantId = Guid.Parse("4fe95e2d-a943-444e-99aa-396d141b03ec"); + + //When + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByParticipantId(participantId)).ToList(); + + //Then + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(2)); + Assert.Multiple(() => + { + Assert.That(playOffers[0].Id, Is.EqualTo(Guid.Parse("9b0e6c95-1a00-49a0-9414-f83ed0eb388f"))); + Assert.That(playOffers[1].Id, Is.EqualTo(Guid.Parse("d71ad67e-fa99-4ef2-b3ee-c6be640607a8"))); + }); + } + + [Test] + public async Task CreatePlayOfferTest() + { + //Given + var playOfferId = Guid.NewGuid(); + var newPlayOffer = new PlayOffer + { + Id = playOfferId, + ClubId = Guid.Parse("34f13619-14b5-4244-a74b-6a8ba210a0b1"), + CreatorId = Guid.Parse("9b30e631-3ad8-4437-a934-94252d6294c4"), + }; + + //When + TestPlayOfferRepository.CreatePlayOffer(newPlayOffer); + await TestPlayOfferRepository.Update(); + + //Then + var playOffers = (await TestPlayOfferRepository.GetPlayOffersByIds(playOfferId)).ToList(); + + Assert.That(playOffers, Is.Not.Null); + Assert.That(playOffers, Has.Count.EqualTo(1)); + Assert.That(playOffers.First().Id, Is.EqualTo(playOfferId)); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/ReadEventRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/ReadEventRepositoryTest.cs new file mode 100644 index 0000000..0c1bfb6 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/ReadEventRepositoryTest.cs @@ -0,0 +1,112 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Member; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class ReadEventRepositoryTest : TestSetup +{ + [SetUp] + public async Task ReadEventSetup() + { + var existingEvent = new BaseEvent + { + EntityId = Guid.Parse("eca14668-95c5-4e40-8f24-7afc155b39a1"), + EntityType = EntityType.MEMBER, + EventId = Guid.Parse("1609fbee-e9e2-4485-a83b-1db7ba78f384"), + EventType = EventType.MEMBER_REGISTERED, + Timestamp = DateTime.UtcNow, + EventData = new MemberCreatedEvent + { + Email = "testemail@gmail.com", + MemberId = new MemberId { Id = Guid.Parse("3ba41c4b-2b0e-4263-b847-518869978e4d") }, + Name = new FullName { FirstName = "Max", LastName = "Mustermann" }, + Status = Status.ACTIVE, + TennisClubId = new TennisClubId { Id = Guid.Parse("9af8b30c-865c-4aa2-9590-9a81ab66d7a1") } + } + }; + await TestReadEventRepository.AppendEvent(existingEvent); + await TestReadEventRepository.Update(); + } + + [Test] + public async Task GetExistingEventByIdTest() + { + // Given + var eventId = Guid.Parse("1609fbee-e9e2-4485-a83b-1db7ba78f384"); + + // When + var baseEvent = await TestReadEventRepository.GetEventById(eventId); + + // Then + Assert.That(baseEvent, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(baseEvent!.EntityId, Is.EqualTo(Guid.Parse("eca14668-95c5-4e40-8f24-7afc155b39a1"))); + Assert.That(baseEvent.EntityType, Is.EqualTo(EntityType.MEMBER)); + Assert.That(baseEvent.EventId, Is.EqualTo(Guid.Parse("1609fbee-e9e2-4485-a83b-1db7ba78f384"))); + Assert.That(baseEvent.EventType, Is.EqualTo(EventType.MEMBER_REGISTERED)); + Assert.That(baseEvent.Timestamp, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(baseEvent.EventData, Is.TypeOf()); + var eventData = baseEvent.EventData as MemberCreatedEvent; + Assert.That(eventData!.Email, Is.EqualTo("testemail@gmail.com")); + Assert.That(eventData.MemberId.Id, Is.EqualTo(Guid.Parse("3ba41c4b-2b0e-4263-b847-518869978e4d"))); + Assert.That(eventData.Name.FirstName, Is.EqualTo("Max")); + Assert.That(eventData.Name.LastName, Is.EqualTo("Mustermann")); + Assert.That(eventData.Status, Is.EqualTo(Status.ACTIVE)); + Assert.That(eventData.TennisClubId.Id, Is.EqualTo(Guid.Parse("9af8b30c-865c-4aa2-9590-9a81ab66d7a1"))); + }); + } + + [Test] + public async Task GetNonExistingEventByIdTest() + { + // Given + var eventId = Guid.Parse("00000000-0000-0000-0000-000000000000"); + + // When + var baseEvent = await TestReadEventRepository.GetEventById(eventId); + + // Then + Assert.That(baseEvent, Is.Null); + } + + [Test] + public async Task AppendEventTest() + { + // Given + var givenEvent = new BaseEvent + { + EntityId = Guid.Parse("481241b5-7435-4019-bb6d-e5bf3af4a376"), + EntityType = EntityType.MEMBER, + EventId = Guid.Parse("73b9f922-ea88-4764-9f0f-7cf132336866"), + EventType = EventType.MEMBER_REGISTERED, + Timestamp = DateTime.UtcNow, + EventData = new MemberCreatedEvent + { + Email = "testemail@gmail.com", + MemberId = new MemberId { Id = Guid.Parse("93b12459-c749-4eda-8c3d-5196946e4a25") }, + Name = new FullName { FirstName = "Max", LastName = "Mustermann" }, + Status = Status.ACTIVE, + TennisClubId = new TennisClubId { Id = Guid.Parse("148c8967-e1fb-4e43-a4a9-8a4e33e2c2ce") } + } + }; + + // When + await TestReadEventRepository.AppendEvent(givenEvent); + await TestReadEventRepository.Update(); + + // Then + var baseEvent = await TestReadEventRepository.GetEventById(givenEvent.EventId); + Assert.That(baseEvent, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(baseEvent!.EntityId, Is.EqualTo(Guid.Parse("481241b5-7435-4019-bb6d-e5bf3af4a376"))); + Assert.That(baseEvent.EntityType, Is.EqualTo(EntityType.MEMBER)); + Assert.That(baseEvent.EventId, Is.EqualTo(Guid.Parse("73b9f922-ea88-4764-9f0f-7cf132336866"))); + Assert.That(baseEvent.EventType, Is.EqualTo(EventType.MEMBER_REGISTERED)); + Assert.That(baseEvent.Timestamp, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs new file mode 100644 index 0000000..2c8a74b --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs @@ -0,0 +1,119 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Member; +using PlayOfferService.Domain.Events.Reservation; +using PlayOfferService.Domain.Models; +using PlayOfferService.Domain.ValueObjects; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class ReservationEventHandlerTest : TestSetup +{ + [SetUp] + public async Task ReservationSetup() + { + var existingReservation = new Reservation + { + Id = Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"), + CourtId = Guid.Parse("5b0ae85c-97fa-4e2d-900f-c5d9239f64b5"), + ReservantId = Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"), + StartTime = DateTime.UtcNow, + EndTime = DateTime.UtcNow.AddHours(1), + ParticipantsIds = new List {Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"), Guid.Parse("13158d40-c04f-49e8-aa0c-b2473145ceaf")}, + IsCancelled = false + }; + TestReservationRepository.CreateReservation(existingReservation); + await TestReservationRepository.Update(); + + var existingPlayOffer = new PlayOffer + { + Id = Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f"), + ClubId = Guid.Parse("6031061f-86c6-459b-9260-5b4772bae9d3"), + CreatorId = Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"), + ProposedStartTime = DateTime.UtcNow, + ProposedEndTime = DateTime.UtcNow.AddHours(3), + AcceptedStartTime = DateTime.UtcNow.AddHours(1), + ReservationId = Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"), + OpponentId = Guid.Parse("13158d40-c04f-49e8-aa0c-b2473145ceaf"), + IsCancelled = false + }; + TestPlayOfferRepository.CreatePlayOffer(existingPlayOffer); + await TestPlayOfferRepository.Update(); + } + + [Test] + public async Task ReservationCreatedEvent_ProjectionTest() + { + //Given + var reservationCreatedEvent = new TechnicalReservationEvent + { + EntityId = Guid.Parse("96e78720-94e2-413e-84e4-795daefa040f"), + EntityType = EntityType.Reservation, + EventId = Guid.Parse("e8d1ed39-6f72-405e-b4e2-0f72bffbf128"), + EventType = EventType.ReservationCreatedEvent, + EventData = new ReservationCreatedEvent + { + CourtId = Guid.Parse("e23e04e1-392e-4907-a96e-9adaec407078"), + ReservantId = Guid.Parse("0f26a0e3-a23b-425d-b54c-f6952c357198"), + Start = DateTime.UtcNow, + End = DateTime.UtcNow.AddHours(1), + ParticipantIds = [Guid.Parse("0f26a0e3-a23b-425d-b54c-f6952c357198")], + TournamentId = Guid.NewGuid(), + } + }; + + //When + await Mediator.Send(reservationCreatedEvent); + + //When + var projectedReservation = await TestReservationRepository.GetReservationById(reservationCreatedEvent.EntityId); + + //Then + Assert.That(projectedReservation, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedReservation!.Id, Is.EqualTo(Guid.Parse("96e78720-94e2-413e-84e4-795daefa040f"))); + Assert.That(projectedReservation.CourtId, Is.EqualTo(Guid.Parse("e23e04e1-392e-4907-a96e-9adaec407078"))); + Assert.That(projectedReservation.ReservantId, Is.EqualTo(Guid.Parse("0f26a0e3-a23b-425d-b54c-f6952c357198"))); + Assert.That(projectedReservation.StartTime, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(projectedReservation.EndTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(projectedReservation.ParticipantsIds, Is.EqualTo(new List {Guid.Parse("0f26a0e3-a23b-425d-b54c-f6952c357198")})); + Assert.That(projectedReservation.IsCancelled, Is.False); + }); + } + + [Test] + public async Task ReservationCancelledEvent_ProjectionTest() + { + // Given + var reservationCancelledEvent = new TechnicalReservationEvent + { + EntityId = Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"), + EntityType = EntityType.Reservation, + EventId = Guid.Parse("25a7fc3e-512b-47f6-b91b-e1d2e48c10cb"), + EventType = EventType.ReservationCancelledEvent, + EventData = new ReservationCancelledEvent() + }; + + // When + await Mediator.Send(reservationCancelledEvent); + + // Then + var playOfferEvents = await TestWriteEventRepository.GetEventByEntityId(Guid.Parse("f515dc74-46ad-4a7b-b4f2-8dfda4b7506f")); + Assert.That(playOfferEvents, Has.Count.EqualTo(1)); + Assert.That(playOfferEvents[0].EventType, Is.EqualTo(EventType.PLAYOFFER_CANCELLED)); + + var projectedReservation = await TestReservationRepository.GetReservationById(reservationCancelledEvent.EntityId); + + Assert.That(projectedReservation, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(projectedReservation!.Id, Is.EqualTo(Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"))); + Assert.That(projectedReservation.CourtId, Is.EqualTo(Guid.Parse("5b0ae85c-97fa-4e2d-900f-c5d9239f64b5"))); + Assert.That(projectedReservation.ReservantId, Is.EqualTo(Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"))); + Assert.That(projectedReservation.StartTime, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(projectedReservation.EndTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(projectedReservation.ParticipantsIds, Is.EqualTo(new List {Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"), Guid.Parse("13158d40-c04f-49e8-aa0c-b2473145ceaf")})); + Assert.That(projectedReservation.IsCancelled, Is.True); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs new file mode 100644 index 0000000..1a8e145 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs @@ -0,0 +1,90 @@ +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class ReservationRepositoryTest : TestSetup +{ + [SetUp] + public async Task ReservationSetup() + { + var existingReservation = new Reservation + { + Id = Guid.Parse("a3f6b4bc-b3cd-49da-9663-3cdb9c9a60bb"), + CourtId = Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a"), + ReservantId = Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13"), + StartTime = DateTime.UtcNow, + EndTime = DateTime.UtcNow.AddHours(1), + ParticipantsIds = [Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13"), Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca")], + IsCancelled = false + }; + TestReservationRepository.CreateReservation(existingReservation); + await TestReservationRepository.Update(); + } + + [Test] + public async Task GetExistingReservationByIdTest() + { + //Given + var reservationId = Guid.Parse("a3f6b4bc-b3cd-49da-9663-3cdb9c9a60bb"); + + //When + var reservation = await TestReservationRepository.GetReservationById(reservationId); + + //Then + Assert.That(reservation, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(reservation!.Id, Is.EqualTo(reservationId)); + Assert.That(reservation.CourtId, Is.EqualTo(Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a"))); + Assert.That(reservation.ReservantId, Is.EqualTo(Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13"))); + Assert.That(reservation.StartTime, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(reservation.EndTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(reservation.ParticipantsIds, Is.EqualTo(new List {Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13"), Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca")})); + Assert.That(reservation.IsCancelled, Is.False); + }); + } + + [Test] + public async Task GetNonExistingReservationByIdTest() + { + //Given + var reservationId = Guid.NewGuid(); + + //When + var reservation = await TestReservationRepository.GetReservationById(reservationId); + + //Then + Assert.That(reservation, Is.Null); + } + + [Test] + public async Task CreateReservationTest() + { + //Given + var givenReservation = new Reservation + { + Id = Guid.Parse("8ef448d9-5099-4226-8d12-582165c4f5e9"), + CourtId = Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a"), + ReservantId = Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca"), + ParticipantsIds = [Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca"),Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13")], + IsCancelled = false + }; + + //When + TestReservationRepository.CreateReservation(givenReservation); + await TestReservationRepository.Update(); + + //Then + var reservation = await TestReservationRepository.GetReservationById(givenReservation.Id); + + Assert.That(reservation, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(reservation!.Id, Is.EqualTo(Guid.Parse("8ef448d9-5099-4226-8d12-582165c4f5e9"))); + Assert.That(reservation.CourtId, Is.EqualTo(Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a"))); + Assert.That(reservation.ReservantId, Is.EqualTo(Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca"))); + Assert.That(reservation.ParticipantsIds, Is.EqualTo(new List {Guid.Parse("20f2e28c-db33-4962-93ce-218afd311fca"),Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13")})); + Assert.That(reservation.IsCancelled, Is.False); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/IntegrationTests/WriteEventRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/WriteEventRepositoryTest.cs new file mode 100644 index 0000000..9ebf727 --- /dev/null +++ b/PlayOfferService.Tests/IntegrationTests/WriteEventRepositoryTest.cs @@ -0,0 +1,140 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.PlayOffer; + +namespace PlayOfferService.Tests.IntegrationTests; + +public class WriteEventRepositoryTest : TestSetup +{ + [SetUp] + public async Task WriteEventSetup() + { + var existingEvent = new BaseEvent + { + EntityId = Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"), + EntityType = EntityType.PLAYOFFER, + EventId = Guid.Parse("3b84c030-5d1e-462e-8989-a8bb1407a41c"), + EventType = EventType.PLAYOFFER_CREATED, + Timestamp = DateTime.UtcNow, + EventData = new PlayOfferCreatedEvent + { + Id = Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"), + ClubId = Guid.Parse("b5aa9ecc-09a8-49ec-8d98-305b9a3ff772"), + CreatorId = Guid.Parse("be7cb6a5-612f-4aa3-9f6e-2c25d812ed49"), + ProposedStartTime = DateTime.UtcNow.AddHours(1), + ProposedEndTime = DateTime.UtcNow.AddHours(3) + } + }; + + await TestWriteEventRepository.AppendEvent(existingEvent); + await TestWriteEventRepository.Update(); + } + + [Test] + public async Task GetExistingEventByIdTest() + { + // Given + var eventId = Guid.Parse("3b84c030-5d1e-462e-8989-a8bb1407a41c"); + + // When + var baseEvent = await TestWriteEventRepository.GetEventById(eventId); + + // Then + Assert.That(baseEvent, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(baseEvent!.EntityId, Is.EqualTo(Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"))); + Assert.That(baseEvent.EntityType, Is.EqualTo(EntityType.PLAYOFFER)); + Assert.That(baseEvent.EventId, Is.EqualTo(Guid.Parse("3b84c030-5d1e-462e-8989-a8bb1407a41c"))); + Assert.That(baseEvent.EventType, Is.EqualTo(EventType.PLAYOFFER_CREATED)); + Assert.That(baseEvent.Timestamp, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(baseEvent.EventData, Is.TypeOf()); + var eventData = baseEvent.EventData as PlayOfferCreatedEvent; + Assert.That(eventData!.Id, Is.EqualTo(Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"))); + Assert.That(eventData.ClubId, Is.EqualTo(Guid.Parse("b5aa9ecc-09a8-49ec-8d98-305b9a3ff772"))); + Assert.That(eventData.CreatorId, Is.EqualTo(Guid.Parse("be7cb6a5-612f-4aa3-9f6e-2c25d812ed49"))); + Assert.That(eventData.ProposedStartTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(eventData.ProposedEndTime, Is.EqualTo(DateTime.UtcNow.AddHours(3)).Within(1).Seconds); + }); + } + + [Test] + public async Task GetExistingEventByEntityIdTest() + { + // Given + var entityId = Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"); + + // When + var baseEvents = await TestWriteEventRepository.GetEventByEntityId(entityId); + + // Then + Assert.That(baseEvents, Has.Count.EqualTo(1)); + var baseEvent = baseEvents.First(); + + Assert.Multiple(() => + { + Assert.That(baseEvent!.EntityId, Is.EqualTo(Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"))); + Assert.That(baseEvent.EntityType, Is.EqualTo(EntityType.PLAYOFFER)); + Assert.That(baseEvent.EventId, Is.EqualTo(Guid.Parse("3b84c030-5d1e-462e-8989-a8bb1407a41c"))); + Assert.That(baseEvent.EventType, Is.EqualTo(EventType.PLAYOFFER_CREATED)); + Assert.That(baseEvent.Timestamp, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(baseEvent.EventData, Is.TypeOf()); + var eventData = baseEvent.EventData as PlayOfferCreatedEvent; + Assert.That(eventData!.Id, Is.EqualTo(Guid.Parse("e77304ae-421f-445c-a0e6-bfee24e6ba60"))); + Assert.That(eventData.ClubId, Is.EqualTo(Guid.Parse("b5aa9ecc-09a8-49ec-8d98-305b9a3ff772"))); + Assert.That(eventData.CreatorId, Is.EqualTo(Guid.Parse("be7cb6a5-612f-4aa3-9f6e-2c25d812ed49"))); + Assert.That(eventData.ProposedStartTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(eventData.ProposedEndTime, Is.EqualTo(DateTime.UtcNow.AddHours(3)).Within(1).Seconds); + }); + } + + [Test] + public async Task GetNonExistingEventByIdTest() + { + // Given + var eventId = Guid.Parse("00000000-0000-0000-0000-000000000000"); + + // When + var baseEvent = await TestWriteEventRepository.GetEventById(eventId); + + // Then + Assert.That(baseEvent, Is.Null); + } + + [Test] + public async Task AppendEventTest() + { + // Given + var givenEvent = new BaseEvent + { + EntityId = Guid.Parse("8bd27cfb-f84d-41e7-8738-901e27b97555"), + EntityType = EntityType.PLAYOFFER, + EventId = Guid.Parse("94dfa918-80f3-4dd7-9cb1-78ec4c71ce2e"), + EventType = EventType.PLAYOFFER_CREATED, + Timestamp = DateTime.UtcNow, + EventData = new PlayOfferCreatedEvent + { + Id = Guid.Parse("8bd27cfb-f84d-41e7-8738-901e27b97555"), + ClubId = Guid.Parse("09b884f1-9fbb-4d39-b3a0-a8f676bc00e3"), + CreatorId = Guid.Parse("cd03259c-e584-46d1-b79c-3dd25ac4ed93"), + ProposedStartTime = DateTime.UtcNow.AddHours(1), + ProposedEndTime = DateTime.UtcNow.AddHours(3) + } + }; + + // When + await TestWriteEventRepository.AppendEvent(givenEvent); + await TestWriteEventRepository.Update(); + + // Then + var baseEvent = await TestWriteEventRepository.GetEventById(givenEvent.EventId); + Assert.That(baseEvent, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(baseEvent!.EntityId, Is.EqualTo(Guid.Parse("8bd27cfb-f84d-41e7-8738-901e27b97555"))); + Assert.That(baseEvent.EntityType, Is.EqualTo(EntityType.PLAYOFFER)); + Assert.That(baseEvent.EventId, Is.EqualTo(Guid.Parse("94dfa918-80f3-4dd7-9cb1-78ec4c71ce2e"))); + Assert.That(baseEvent.EventType, Is.EqualTo(EventType.PLAYOFFER_CREATED)); + Assert.That(baseEvent.Timestamp, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/TestSetup.cs b/PlayOfferService.Tests/TestSetup.cs index 139a768..4f9d4cc 100644 --- a/PlayOfferService.Tests/TestSetup.cs +++ b/PlayOfferService.Tests/TestSetup.cs @@ -1,3 +1,4 @@ +using MediatR; using Microsoft.Extensions.DependencyInjection; using PlayOfferService.Domain; using PlayOfferService.Domain.Repositories; @@ -14,7 +15,12 @@ public class TestSetup private PostgreSqlContainer _writeDbContainer; protected ClubRepository TestClubRepository; protected MemberRepository TestMemberRepository; - protected HttpClient HttpClient; + protected PlayOfferRepository TestPlayOfferRepository; + protected ReservationRepository TestReservationRepository; + protected CourtRepository TestCourtRepository; + protected ReadEventRepository TestReadEventRepository; + protected WriteEventRepository TestWriteEventRepository; + protected IMediator Mediator; [SetUp] public async Task Setup() @@ -40,7 +46,6 @@ public async Task Setup() await _writeDbContainer.StartAsync(); _factory = new WebAppFactory(_readDbContainer.GetConnectionString(), _writeDbContainer.GetConnectionString()); - HttpClient = _factory.CreateClient(); var scopeFactory = _factory.Services.GetService() ?? throw new Exception("Scope factory not found"); @@ -57,6 +62,24 @@ public async Task Setup() TestMemberRepository = scope.ServiceProvider.GetService() ?? throw new Exception("Could not get MemberRepository"); + + TestPlayOfferRepository = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get PlayOfferRepository"); + + TestReservationRepository = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get ReservationRepository"); + + TestCourtRepository = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get CourtRepository"); + + TestReadEventRepository = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get ReadEventRepository"); + + TestWriteEventRepository = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get WriteEventRepository"); + + Mediator = scope.ServiceProvider.GetService() ?? + throw new Exception("Could not get IMediator"); _readContext.Database.EnsureCreated(); _writeContext.Database.EnsureCreated(); @@ -65,7 +88,6 @@ public async Task Setup() [TearDown] public async Task TearDown() { - HttpClient.Dispose(); await _factory.DisposeAsync(); await _readContext.DisposeAsync(); await _writeContext.DisposeAsync(); diff --git a/PlayOfferService.Tests/UnitTests/ClubUnitTest.cs b/PlayOfferService.Tests/UnitTests/ClubUnitTest.cs index b3e109c..7e9fcab 100644 --- a/PlayOfferService.Tests/UnitTests/ClubUnitTest.cs +++ b/PlayOfferService.Tests/UnitTests/ClubUnitTest.cs @@ -1,5 +1,5 @@ using PlayOfferService.Domain.Events; -using PlayOfferService.Domain.Events.Member; +using PlayOfferService.Domain.Events.Club; using PlayOfferService.Domain.Models; using PlayOfferService.Domain.ValueObjects; @@ -21,7 +21,7 @@ public void ApplyClubCreatedEventTest() EventType = EventType.TENNIS_CLUB_REGISTERED, EventData = new ClubCreatedEvent { - TennisClubId = new TennisClubId{Id=clubId} + TennisClubId = new TennisClubId { Id = clubId } } }; @@ -82,7 +82,7 @@ public void ApplyClubUnlockEventTest() // Then Assert.That(club.Status, Is.EqualTo(Status.ACTIVE)); } - + [Test] public void ApplyClubDeletedEventTest() { @@ -106,4 +106,33 @@ public void ApplyClubDeletedEventTest() // Then Assert.That(club.Status, Is.EqualTo(Status.DELETED)); } + + [Test] + public void ApplyClubNameChangedEventTest() + { + // Given + var club = new Club + { + Id = Guid.NewGuid(), + Name = "Test Club" + }; + var clubEvents = new List + { + new() + { + EventType = EventType.TENNIS_CLUB_NAME_CHANGED, + EventData = new ClubNameChangedEvent + { + Name = "New Club Name" + } + } + }; + + // When + club.Apply(clubEvents); + + // Then + Assert.That(club.Name, Is.EqualTo("New Club Name")); + } + } \ No newline at end of file diff --git a/PlayOfferService.Tests/UnitTests/CourtUnitTest.cs b/PlayOfferService.Tests/UnitTests/CourtUnitTest.cs new file mode 100644 index 0000000..905a0bb --- /dev/null +++ b/PlayOfferService.Tests/UnitTests/CourtUnitTest.cs @@ -0,0 +1,60 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Court; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.UnitTests; + +public class CourtUnitTest +{ + [Test] + public void ApplyCourtCreatedEventTest() + { + // Given + var courtCreatedEvent = new BaseEvent { + EventType = EventType.CourtCreatedEvent, + EntityId = Guid.Parse("d2c0c38c-2680-41ad-a50a-6e9a18408c8b"), + EventData = new CourtCreatedEvent { + ClubId = Guid.Parse("353b10b4-97d6-4eff-aef3-7a4a37db91ff"), + Name = "TestCourt 1" + } + }; + + // When + var court = new Court(); + court.Apply([courtCreatedEvent]); + + // Then + Assert.Multiple(() => { + Assert.That(court.Id, Is.EqualTo(Guid.Parse("d2c0c38c-2680-41ad-a50a-6e9a18408c8b"))); + Assert.That(court.ClubId, Is.EqualTo(Guid.Parse("353b10b4-97d6-4eff-aef3-7a4a37db91ff"))); + Assert.That(court.Name, Is.EqualTo("TestCourt 1")); + }); + } + + public void ApplyCourtUpdatedEventTest() + { + // Given + var courtUpdatedEvent = new BaseEvent { + EventType = EventType.CourtUpdatedEvent, + EntityId = Guid.Parse("bb50fb5c-534b-47df-ac91-29eec0447e9b"), + EventData = new CourtUpdatedEvent { + CourtName = "TestCourt 1 - Updated" + } + }; + var givenCourt = new Court { + Id = Guid.Parse("bb50fb5c-534b-47df-ac91-29eec0447e9b"), + ClubId = Guid.Parse("724ecde4-7da9-480f-b84d-95ba4346ade9"), + Name = "TestCourt 1" + }; + + // When + givenCourt.Apply([courtUpdatedEvent]); + + // Then + Assert.Multiple(() => { + Assert.That(givenCourt.Id, Is.EqualTo(Guid.Parse("bb50fb5c-534b-47df-ac91-29eec0447e9b"))); + Assert.That(givenCourt.ClubId, Is.EqualTo(Guid.Parse("724ecde4-7da9-480f-b84d-95ba4346ade9"))); + Assert.That(givenCourt.Name, Is.EqualTo("TestCourt 1 - Updated")); + }); + } +} \ No newline at end of file diff --git a/PlayOfferService.Tests/UnitTests/MemberUnitTest.cs b/PlayOfferService.Tests/UnitTests/MemberUnitTest.cs index c57d8d7..a18c9ea 100644 --- a/PlayOfferService.Tests/UnitTests/MemberUnitTest.cs +++ b/PlayOfferService.Tests/UnitTests/MemberUnitTest.cs @@ -22,21 +22,22 @@ public void ApplyMemberCreatedEventTest() EventType = EventType.MEMBER_REGISTERED, EventData = new MemberCreatedEvent { - MemberId = new MemberId{Id=memberId}, - TennisClubId = new TennisClubId{Id=clubId} + MemberId = new MemberId { Id = memberId }, + Name = new FullName { FirstName = "John", LastName = "Doe" }, + TennisClubId = new TennisClubId { Id = clubId } } }; - + // When var member = new Member(); member.Apply([memberCreationEvent]); - + // Then Assert.That(member.Id, Is.EqualTo(memberId)); Assert.That(member.ClubId, Is.EqualTo(clubId)); Assert.That(member.Status, Is.EqualTo(Status.ACTIVE)); } - + [Test] public void ApplyMemberLockEventTest() { @@ -54,14 +55,14 @@ public void ApplyMemberLockEventTest() EventData = new MemberLockedEvent() } }; - + // When member.Apply(memberEvents); - + // Then Assert.That(member.Status, Is.EqualTo(Status.LOCKED)); } - + [Test] public void ApplyMemberUnlockEventTest() { @@ -79,14 +80,14 @@ public void ApplyMemberUnlockEventTest() EventData = new MemberUnlockedEvent() } }; - + // When member.Apply(memberEvents); - + // Then Assert.That(member.Status, Is.EqualTo(Status.ACTIVE)); } - + [Test] public void ApplyMemberDeleteEventTest() { @@ -104,11 +105,74 @@ public void ApplyMemberDeleteEventTest() EventData = new MemberDeletedEvent() } }; - + // When member.Apply(memberEvents); - + // Then Assert.That(member.Status, Is.EqualTo(Status.DELETED)); } + + [Test] + public void ApplyMemberNameChangedEventTest() + { + // Given + var member = new Member + { + Id = Guid.NewGuid(), + FirstName = "John", + LastName = "Doe" + }; + + var memberEvents = new List + { + new() + { + EventType = EventType.MEMBER_FULL_NAME_CHANGED, + EventData = new MemberFullNameChangedEvent + { + FullName = new FullName { FirstName = "Jane", LastName = "Doe" } + } + } + }; + + // When + member.Apply(memberEvents); + + // Then + Assert.That(member.FirstName, Is.EqualTo("Jane")); + Assert.That(member.LastName, Is.EqualTo("Doe")); + } + + [Test] + public void ApplyMemberEmailChangedEvent() + { + // Given + var member = new Member + { + Id = Guid.NewGuid(), + FirstName = "John", + LastName = "Doe", + Email = "test@test.test" + }; + + var memberEvents = new List + { + new() + { + EventType = EventType.MEMBER_EMAIL_CHANGED, + EventData = new MemberEmailChangedEvent + { + Email = "changedEmail@com.com" + } + + } + }; + + //When + member.Apply(memberEvents); + + //Then + Assert.That(member.Email, Is.EqualTo("changedEmail@com.com")); + } } \ No newline at end of file diff --git a/PlayOfferService.Tests/UnitTests/PlayOfferUnitTest.cs b/PlayOfferService.Tests/UnitTests/PlayOfferUnitTest.cs index eb99298..9150a77 100644 --- a/PlayOfferService.Tests/UnitTests/PlayOfferUnitTest.cs +++ b/PlayOfferService.Tests/UnitTests/PlayOfferUnitTest.cs @@ -44,21 +44,15 @@ public void ApplyPlayOfferCreatedEventTest() public void ApplyPlayOfferCancelledEvent() { // Given - var playOffer = new PlayOffer + var playOfferEvent = new BaseEvent { - Id = Guid.NewGuid() - }; - var playOfferEvents = new List - { - new() - { - EventType = EventType.PLAYOFFER_CANCELLED, - EventData = new PlayOfferCancelledEvent() - } + EventType = EventType.PLAYOFFER_CANCELLED, + EventData = new PlayOfferCancelledEvent() }; // When - playOffer.Apply(playOfferEvents); + var playOffer = new PlayOffer(); + playOffer.Apply([playOfferEvent]); // Then Assert.That(playOffer.IsCancelled, Is.True); @@ -70,38 +64,66 @@ public void ApplyPlayOfferJoinedEvent() // Given var opponentId = Guid.NewGuid(); var acceptedStartTime = DateTime.UtcNow.AddHours(3); - var clubId = Guid.NewGuid(); - var playOfferEvents = new List - { - new() + var playOfferEvent = new BaseEvent { + EventType = EventType.PLAYOFFER_JOINED, + EventData = new PlayOfferJoinedEvent { - EventType = EventType.PLAYOFFER_CREATED, - EventData = new PlayOfferCreatedEvent - { - CreatorId = Guid.NewGuid(), - ClubId = clubId, - ProposedStartTime = DateTime.UtcNow.AddHours(2), - ProposedEndTime = DateTime.UtcNow.AddHours(5) - } - }, - new() - { - EventType = EventType.PLAYOFFER_JOINED, - EventData = new PlayOfferJoinedEvent - { - OpponentId = opponentId, - AcceptedStartTime = acceptedStartTime - } + OpponentId = opponentId, + AcceptedStartTime = acceptedStartTime } }; // When var playOffer = new PlayOffer(); - playOffer.Apply(playOfferEvents); + playOffer.Apply([playOfferEvent]); // Then Assert.That(playOffer.OpponentId, Is.EqualTo(opponentId)); Assert.That(playOffer.AcceptedStartTime, Is.EqualTo(acceptedStartTime)); } + + [Test] + public void ApplyPlayOfferReservationAddedEvent() + { + // Given + var reservationId = Guid.NewGuid(); + var playOfferEvent = new BaseEvent + { + EventType = EventType.PLAYOFFER_RESERVATION_ADDED, + EventData = new PlayOfferReservationAddedEvent + { + ReservationId = reservationId + } + }; + + // When + var playOffer = new PlayOffer(); + playOffer.Apply([playOfferEvent]); + + // Then + Assert.That(playOffer.ReservationId, Is.EqualTo(reservationId)); + } + + [Test] + public void ApplyPlayOfferOpponentRemovedEvent() + { + // Given + var givenPlayOffer = new PlayOffer + { + OpponentId = Guid.NewGuid() + }; + + var playOfferEvent = new BaseEvent + { + EventType = EventType.PLAYOFFER_OPPONENT_REMOVED, + EventData = new PlayOfferOpponentRemovedEvent() + }; + + // When + givenPlayOffer.Apply([playOfferEvent]); + + // Then + Assert.That(givenPlayOffer.OpponentId, Is.Null); + } } \ No newline at end of file diff --git a/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs b/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs new file mode 100644 index 0000000..c5de6ac --- /dev/null +++ b/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs @@ -0,0 +1,76 @@ +using PlayOfferService.Domain.Events; +using PlayOfferService.Domain.Events.Reservation; +using PlayOfferService.Domain.Models; + +namespace PlayOfferService.Tests.UnitTests; + +public class ReservationUnitTest +{ + [Test] + public void ApplyReservationCreatedEvent() + { + // Given + var reservationCreatedEvent = new BaseEvent + { + EntityId = Guid.Parse("b68ff7a7-94a0-4807-80bc-7b5a0a75264f"), + EntityType = EntityType.Reservation, + EventId = Guid.Parse("29cfce18-81df-4741-96b0-2ac129877d07"), + EventType = EventType.ReservationCreatedEvent, + EventData = new ReservationCreatedEvent + { + CourtId = Guid.Parse("b67599e8-f8d7-4956-bbfc-be70c69d7b4b"), + ReservantId = Guid.Parse("fd834838-748a-435c-a3a4-a4e93b2d4f40"), + Start = DateTime.UtcNow, + End = DateTime.UtcNow.AddHours(1), + ParticipantIds = [Guid.Parse("ad51b156-fa81-4403-a8c3-69c8908f31f8")], + TournamentId = Guid.NewGuid(), + } + }; + + // When + var reservation = new Reservation(); + reservation.Apply([reservationCreatedEvent]); + + // Then + Assert.Multiple(() => + { + Assert.That(reservation.Id, Is.EqualTo(Guid.Parse("b68ff7a7-94a0-4807-80bc-7b5a0a75264f"))); + Assert.That(reservation.CourtId, Is.EqualTo(Guid.Parse("b67599e8-f8d7-4956-bbfc-be70c69d7b4b"))); + Assert.That(reservation.ReservantId, Is.EqualTo(Guid.Parse("fd834838-748a-435c-a3a4-a4e93b2d4f40"))); + Assert.That(reservation.StartTime, Is.EqualTo(DateTime.UtcNow).Within(1).Seconds); + Assert.That(reservation.EndTime, Is.EqualTo(DateTime.UtcNow.AddHours(1)).Within(1).Seconds); + Assert.That(reservation.ParticipantsIds, Is.EqualTo(new List {Guid.Parse("ad51b156-fa81-4403-a8c3-69c8908f31f8")})); + Assert.That(reservation.IsCancelled, Is.False); + }); + } + + [Test] + public void ApplyReservationCancelledEvent() + { + // Given + var givenReservation = new Reservation + { + Id = Guid.NewGuid(), + CourtId = Guid.NewGuid(), + ReservantId = Guid.NewGuid(), + StartTime = DateTime.UtcNow, + EndTime = DateTime.UtcNow.AddHours(1), + ParticipantsIds = new List {Guid.NewGuid()}, + IsCancelled = false + }; + var reservationCancelledEvent = new BaseEvent + { + EntityId = Guid.Parse("49c52e8a-bb50-46a4-9163-7da430cf64c0"), + EntityType = EntityType.Reservation, + EventId = Guid.Parse("534a9e3b-ebc7-40fa-ad21-cff5bc1827f4"), + EventType = EventType.ReservationCancelledEvent, + EventData = new ReservationCancelledEvent() + }; + + // When + givenReservation.Apply([reservationCancelledEvent]); + + // Then + Assert.That(givenReservation.IsCancelled, Is.True); + } +} \ No newline at end of file diff --git a/PlayOfferService.csproj b/PlayOfferService.csproj index d48a41c..fd058b2 100644 --- a/PlayOfferService.csproj +++ b/PlayOfferService.csproj @@ -18,6 +18,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Program.cs b/Program.cs index eca03af..64b30a4 100644 --- a/Program.cs +++ b/Program.cs @@ -4,6 +4,11 @@ using PlayOfferService.Domain; using PlayOfferService.Domain.Repositories; using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using PlayOfferService.Application.Controllers; var builder = WebApplication.CreateBuilder(args); @@ -27,6 +32,10 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddControllers(); builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); @@ -35,6 +44,25 @@ builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + builder.Services.AddHostedService(); + + var publicKey = File.ReadAllText("publicKeyDev.pem"); + var rsa = RSA.Create(); + rsa.ImportFromPem(publicKey); + var jwtKey = new RsaSecurityKey(rsa); + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = false, + ValidateAudience = false, + ValidateLifetime = false, + ValidateIssuerSigningKey = true, + IssuerSigningKey = jwtKey, + }; + }); } // Swagger configuration @@ -50,6 +78,34 @@ options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml")); + + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme.", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer" + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + }, + Scheme = "oauth2", + Name = "Bearer", + In = ParameterLocation.Header, + + }, + new List() + } + }); }); var app = builder.Build(); @@ -60,20 +116,19 @@ options.SwaggerEndpoint("/swagger/v1/swagger.json", "PlayofferService API v1"); }); -// Create the database if it doesn't exist -if (app.Environment.IsDevelopment()) -{ - using var scope = app.Services.CreateScope(); - var services = scope.ServiceProvider; - var readDbContext = services.GetRequiredService(); - var writeDbContext = services.GetRequiredService(); - - readDbContext.Database.EnsureCreated(); - writeDbContext.Database.EnsureCreated(); -} + +using var scope = app.Services.CreateScope(); +var services = scope.ServiceProvider; +var readDbContext = services.GetRequiredService(); +var writeDbContext = services.GetRequiredService(); + +readDbContext.Database.EnsureCreated(); +writeDbContext.Database.EnsureCreated(); + app.UseHttpsRedirection(); +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); diff --git a/debezium-conf/application.properties b/debezium-conf/application.properties index a44c8ca..5343f49 100644 --- a/debezium-conf/application.properties +++ b/debezium-conf/application.properties @@ -13,7 +13,7 @@ debezium.source.schema.include.list=public # sink config (redis) debezium.sink.type=redis -debezium.sink.redis.address=pos_redis:6379 +debezium.sink.redis.address=redis:6379 #debezium.sink.redis.user=WE HAVE NO USER CONFIGURED SO WE COMMENT THIS #debezium.sink.redis.password=WE HAVE NO PASSWORD CONFIGURED SO WE COMMENT THIS diff --git a/docker-compose.yml b/docker-compose.yml index 437b6a4..99659e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ version: '3.5' services: - pos_redis: + redis: image: redis:7.0-alpine restart: always ports: @@ -36,7 +36,7 @@ services: - "6380:8080" depends_on: - pos_postgres_write - - pos_redis + - redis volumes: - ./debezium-conf:/debezium/conf networks: @@ -52,7 +52,7 @@ services: - pos_postgres_read ports: - "8080:8080" - - "8081:8081" + - "8082:8081" networks: - global_network diff --git a/publicKeyDev.pem b/publicKeyDev.pem new file mode 100644 index 0000000..12a18c1 --- /dev/null +++ b/publicKeyDev.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEq +Fyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwR +TYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5e +UF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9 +AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYn +sIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9x +nQIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file