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 bd1ea7c..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(CreatePlayOfferDto CreatePlayOfferDto) : 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 e822d34..41c176a 100644 --- a/Application/Controllers/PlayOfferController.cs +++ b/Application/Controllers/PlayOfferController.cs @@ -1,6 +1,9 @@ +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; @@ -18,22 +21,22 @@ public PlayOfferController(IMediator mediator) _mediator = mediator; } - /// - ///Retrieve all Play Offers of a club with a matching id - /// - ///The id of the club of the play offer - ///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 + /// + /// 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([FromQuery] Guid clubId) + public async Task>> GetByClubIdAsync() { - //TODO: refactor after jwt implementation to get clubId from token + var clubId = Guid.Parse(User.Claims.First(c => c.Type == "tennisClubId").Value); var result = await _mediator.Send(new GetPlayOffersByClubIdQuery(clubId)); if (result.Count() == 0) @@ -43,21 +46,21 @@ public async Task>> GetByClubIdAsync([Fro } /// - ///Retrieve all Play Offers of a participating member + ///Retrieve all Play Offers of a logged in user /// - ///The id of the member participating in the play offer ///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([FromQuery] Guid participantId) + public async Task>> GetByParticipantIdAsync() { - //TODO: refactor after jwt implementation to get participantId from token + var participantId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value); var result = await _mediator.Send(new GetPlayOffersByParticipantIdQuery(participantId)); if (result.Count() == 0) @@ -74,6 +77,7 @@ public async Task>> GetByParticipantIdAsy ///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)] @@ -100,23 +104,31 @@ public async Task>> GetByCreatorNameAsync /// - ///Create a new Play Offer + ///Create a new Play Offer for the logged in user /// ///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(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(createPlayOfferDto)); + result = await _mediator.Send(new CreatePlayOfferCommand(createPlayOfferDto, creatorId, clubId)); } catch (Exception e) { @@ -127,22 +139,33 @@ public async Task> Create(CreatePlayOfferDto createPlayO } /// - ///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) { @@ -153,13 +176,15 @@ 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] + [Authorize] [Route("join")] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)] @@ -167,9 +192,13 @@ public async Task Delete(Guid playOfferId) [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/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 c84db65..de28b71 100644 --- a/Application/Handlers/CancelPlayOfferHandler.cs +++ b/Application/Handlers/CancelPlayOfferHandler.cs @@ -27,6 +27,9 @@ public async Task Handle(CancelPlayOfferCommand request, CancellationToken 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) diff --git a/Application/Handlers/CreatePlayOfferHandler.cs b/Application/Handlers/CreatePlayOfferHandler.cs index 92aca4b..97e99c3 100644 --- a/Application/Handlers/CreatePlayOfferHandler.cs +++ b/Application/Handlers/CreatePlayOfferHandler.cs @@ -25,9 +25,9 @@ public async Task Handle(CreatePlayOfferCommand request, CancellationToken { var playOfferDto = request.CreatePlayOfferDto; - var club = await _clubRepository.GetClubById(playOfferDto.ClubId); + var club = await _clubRepository.GetClubById(request.ClubId); if(club == null) - throw new NotFoundException($"Club {request.CreatePlayOfferDto.ClubId} not found"); + throw new NotFoundException($"Club {request.ClubId} not found"); switch (club.Status) { case Status.LOCKED: @@ -36,9 +36,9 @@ public async Task Handle(CreatePlayOfferCommand request, CancellationToken throw new InvalidOperationException("Can't create PlayOffer in deleted club!"); } - var creator = await _memberRepository.GetMemberById(playOfferDto.CreatorId); + var creator = await _memberRepository.GetMemberById(request.CreatorId); if(creator == null) - throw new NotFoundException($"Member {request.CreatePlayOfferDto.CreatorId} not found!"); + throw new NotFoundException($"Member {request.CreatorId} not found!"); switch (creator.Status) { case Status.LOCKED: diff --git a/Application/Handlers/GetPlayOffersByClubIdHandler.cs b/Application/Handlers/GetPlayOffersByClubIdHandler.cs index ffd3bd9..3e78837 100644 --- a/Application/Handlers/GetPlayOffersByClubIdHandler.cs +++ b/Application/Handlers/GetPlayOffersByClubIdHandler.cs @@ -28,7 +28,11 @@ public async Task> Handle(GetPlayOffersByClubIdQuery r 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)).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) diff --git a/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs b/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs index 3858e88..a138bdb 100644 --- a/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs +++ b/Application/Handlers/GetPlayOffersByCreatorNameHandler.cs @@ -38,7 +38,11 @@ public async Task> Handle(GetPlayOffersByCreatorNameQu 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)).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) diff --git a/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs b/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs index 78581ec..bf92560 100644 --- a/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs +++ b/Application/Handlers/GetPlayOffersByParticipantIdHandler.cs @@ -28,7 +28,11 @@ public async Task> Handle(GetPlayOffersByParticipantId 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)).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) diff --git a/Application/Handlers/JoinPlayOfferHandler.cs b/Application/Handlers/JoinPlayOfferHandler.cs index c6c98c0..261a2a1 100644 --- a/Application/Handlers/JoinPlayOfferHandler.cs +++ b/Application/Handlers/JoinPlayOfferHandler.cs @@ -30,9 +30,9 @@ public async Task Handle(JoinPlayOfferCommand request, CancellationToken c 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!"); diff --git a/Domain/DbReadContext.cs b/Domain/DbReadContext.cs index 21f8b71..ebd3508 100644 --- a/Domain/DbReadContext.cs +++ b/Domain/DbReadContext.cs @@ -24,8 +24,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.ApplyConfiguration(new BaseEventConfiguration()); // TODO: Remove before coop testing - var testClub = new Club { Id = Guid.Parse("06b812a7-5131-4510-82ff-bffac33e0f3e"), Name = "Test Club", 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, FirstName = "Hans", LastName="Müller", Email="hans@müller.de"}, @@ -37,7 +37,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { Id = Guid.NewGuid(), ClubId = testClub.Id, - CreatorId = testMemberIds[0], + CreatorId = testMemberIds[1], ProposedStartTime = DateTime.UtcNow, ProposedEndTime = DateTime.UtcNow.AddHours(1), IsCancelled = false diff --git a/Domain/Events/Reservation/ReservationCreatedEvent.cs b/Domain/Events/Reservation/ReservationCreatedEvent.cs index ef80ef7..f82e2d2 100644 --- a/Domain/Events/Reservation/ReservationCreatedEvent.cs +++ b/Domain/Events/Reservation/ReservationCreatedEvent.cs @@ -15,7 +15,7 @@ public class ReservationCreatedEvent : DomainEvent [JsonPropertyName("participantIds")] public List? ParticipantIds { get; set; } [JsonPropertyName("courtIds")] - public List CourtIds { get; set; } + public Guid CourtId { get; set; } public ReservationCreatedEvent(){} } diff --git a/Domain/Events/Reservation/ReservationLimitExceededEvent.cs b/Domain/Events/Reservation/ReservationLimitExceededEvent.cs index 8b79906..d19d13b 100644 --- a/Domain/Events/Reservation/ReservationLimitExceededEvent.cs +++ b/Domain/Events/Reservation/ReservationLimitExceededEvent.cs @@ -1,5 +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 index 24a97e3..ad4bbb7 100644 --- a/Domain/Events/Reservation/ReservationRejectedEvent.cs +++ b/Domain/Events/Reservation/ReservationRejectedEvent.cs @@ -1,7 +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/Models/DTOs/CreatePlayOfferDto.cs b/Domain/Models/DTOs/CreatePlayOfferDto.cs index 5b12f8c..efa52c8 100644 --- a/Domain/Models/DTOs/CreatePlayOfferDto.cs +++ b/Domain/Models/DTOs/CreatePlayOfferDto.cs @@ -2,8 +2,6 @@ namespace PlayOfferService.Domain.Models; 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/ReservationDto.cs b/Domain/Models/DTOs/ReservationDto.cs index 6e0b725..1bf37b8 100644 --- a/Domain/Models/DTOs/ReservationDto.cs +++ b/Domain/Models/DTOs/ReservationDto.cs @@ -3,15 +3,15 @@ namespace PlayOfferService.Domain.Models; public class ReservationDto { public Guid Id { get; set; } - public List Courts { 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, List courts) + public ReservationDto(Reservation reservation, CourtDto court) { Id = reservation.Id; - Courts = courts.Where(c => reservation.CourtIds.Contains(c.Id)).ToList(); + Court = court; StartTime = reservation.StartTime; EndTime = reservation.EndTime; IsCancelled = reservation.IsCancelled; 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/Reservation.cs b/Domain/Models/Reservation.cs index 2eaf424..ad37f01 100644 --- a/Domain/Models/Reservation.cs +++ b/Domain/Models/Reservation.cs @@ -8,7 +8,7 @@ public class Reservation { [DatabaseGenerated(DatabaseGeneratedOption.None)] public Guid Id { get; set; } - public List CourtIds { get; set; } + public Guid CourtId { get; set; } public Guid? ReservantId { get; set; } public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } @@ -41,7 +41,7 @@ private void ApplyReservationCreatedEvent(BaseEvent baseEvent) var domainEvent = (ReservationCreatedEvent) baseEvent.EventData; Id = baseEvent.EntityId; - CourtIds = domainEvent.CourtIds; + CourtId = domainEvent.CourtId; ReservantId = domainEvent.ReservantId; StartTime = domainEvent.Start; EndTime = domainEvent.End; 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 f30a568..33815e0 100644 --- a/Migrations/DbReadContextModelSnapshot.cs +++ b/Migrations/DbReadContextModelSnapshot.cs @@ -124,14 +124,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .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"); @@ -149,12 +152,18 @@ 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 }); }); @@ -205,12 +214,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("7d079edb-d4c8-4e63-9c5b-35b80eaa69f6"), + 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, 7, 12, 24, 44, 893, DateTimeKind.Utc).AddTicks(6493), - ProposedStartTime = new DateTime(2024, 6, 7, 11, 24, 44, 893, DateTimeKind.Utc).AddTicks(6491) + 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) }); }); @@ -220,10 +229,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("id"); - b.Property>("CourtIds") - .IsRequired() - .HasColumnType("uuid[]") - .HasColumnName("courtIds"); + b.Property("CourtId") + .HasColumnType("uuid") + .HasColumnName("courtId"); b.Property("EndTime") .HasColumnType("timestamp with time zone") diff --git a/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs b/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs index a8e2fea..2c8a74b 100644 --- a/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs +++ b/PlayOfferService.Tests/IntegrationTests/ReservationEventHandlerTest.cs @@ -14,7 +14,7 @@ public async Task ReservationSetup() var existingReservation = new Reservation { Id = Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"), - CourtIds = new List {Guid.Parse("5b0ae85c-97fa-4e2d-900f-c5d9239f64b5")}, + CourtId = Guid.Parse("5b0ae85c-97fa-4e2d-900f-c5d9239f64b5"), ReservantId = Guid.Parse("844b93a3-fce8-4d8f-8a8d-38da07a9c11f"), StartTime = DateTime.UtcNow, EndTime = DateTime.UtcNow.AddHours(1), @@ -52,7 +52,7 @@ public async Task ReservationCreatedEvent_ProjectionTest() EventType = EventType.ReservationCreatedEvent, EventData = new ReservationCreatedEvent { - CourtIds = [Guid.Parse("e23e04e1-392e-4907-a96e-9adaec407078")], + CourtId = Guid.Parse("e23e04e1-392e-4907-a96e-9adaec407078"), ReservantId = Guid.Parse("0f26a0e3-a23b-425d-b54c-f6952c357198"), Start = DateTime.UtcNow, End = DateTime.UtcNow.AddHours(1), @@ -72,7 +72,7 @@ public async Task ReservationCreatedEvent_ProjectionTest() Assert.Multiple(() => { Assert.That(projectedReservation!.Id, Is.EqualTo(Guid.Parse("96e78720-94e2-413e-84e4-795daefa040f"))); - Assert.That(projectedReservation.CourtIds, Is.EqualTo(new List {Guid.Parse("e23e04e1-392e-4907-a96e-9adaec407078")})); + 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); @@ -108,7 +108,7 @@ public async Task ReservationCancelledEvent_ProjectionTest() Assert.Multiple(() => { Assert.That(projectedReservation!.Id, Is.EqualTo(Guid.Parse("da9ff928-cfb2-4b98-9388-937905556706"))); - Assert.That(projectedReservation.CourtIds, Is.EqualTo(new List {Guid.Parse("5b0ae85c-97fa-4e2d-900f-c5d9239f64b5")})); + 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); diff --git a/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs b/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs index 4e3749e..1a8e145 100644 --- a/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs +++ b/PlayOfferService.Tests/IntegrationTests/ReservationRepositoryTest.cs @@ -10,7 +10,7 @@ public async Task ReservationSetup() var existingReservation = new Reservation { Id = Guid.Parse("a3f6b4bc-b3cd-49da-9663-3cdb9c9a60bb"), - CourtIds = [Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a")], + CourtId = Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a"), ReservantId = Guid.Parse("40337148-538c-44b5-b08a-d3a2cd4ddd13"), StartTime = DateTime.UtcNow, EndTime = DateTime.UtcNow.AddHours(1), @@ -35,7 +35,7 @@ public async Task GetExistingReservationByIdTest() Assert.Multiple(() => { Assert.That(reservation!.Id, Is.EqualTo(reservationId)); - Assert.That(reservation.CourtIds, Is.EqualTo(new List {Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a")})); + 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); @@ -64,7 +64,7 @@ public async Task CreateReservationTest() var givenReservation = new Reservation { Id = Guid.Parse("8ef448d9-5099-4226-8d12-582165c4f5e9"), - CourtIds = [Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a")], + 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 @@ -81,7 +81,7 @@ public async Task CreateReservationTest() Assert.Multiple(() => { Assert.That(reservation!.Id, Is.EqualTo(Guid.Parse("8ef448d9-5099-4226-8d12-582165c4f5e9"))); - Assert.That(reservation.CourtIds, Is.EqualTo(new List {Guid.Parse("cc02023d-9306-471c-b298-8bc584e8ef8a")})); + 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); diff --git a/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs b/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs index ad43746..c5de6ac 100644 --- a/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs +++ b/PlayOfferService.Tests/UnitTests/ReservationUnitTest.cs @@ -18,7 +18,7 @@ public void ApplyReservationCreatedEvent() EventType = EventType.ReservationCreatedEvent, EventData = new ReservationCreatedEvent { - CourtIds = [Guid.Parse("b67599e8-f8d7-4956-bbfc-be70c69d7b4b")], + CourtId = Guid.Parse("b67599e8-f8d7-4956-bbfc-be70c69d7b4b"), ReservantId = Guid.Parse("fd834838-748a-435c-a3a4-a4e93b2d4f40"), Start = DateTime.UtcNow, End = DateTime.UtcNow.AddHours(1), @@ -35,7 +35,7 @@ public void ApplyReservationCreatedEvent() Assert.Multiple(() => { Assert.That(reservation.Id, Is.EqualTo(Guid.Parse("b68ff7a7-94a0-4807-80bc-7b5a0a75264f"))); - Assert.That(reservation.CourtIds, Is.EqualTo(new List {Guid.Parse("b67599e8-f8d7-4956-bbfc-be70c69d7b4b")})); + 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); @@ -51,7 +51,7 @@ public void ApplyReservationCancelledEvent() var givenReservation = new Reservation { Id = Guid.NewGuid(), - CourtIds = new List {Guid.NewGuid()}, + CourtId = Guid.NewGuid(), ReservantId = Guid.NewGuid(), StartTime = DateTime.UtcNow, EndTime = DateTime.UtcNow.AddHours(1), 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 6eb0baa..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); @@ -41,6 +46,23 @@ 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 @@ -56,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(); @@ -78,6 +128,7 @@ app.UseHttpsRedirection(); +app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); 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