Skip to content

Commit 9cd6767

Browse files
authored
Merge pull request #19 from THC-Software/jwt_implementation
Jwt implementation
2 parents 0253914 + f5452d3 commit 9cd6767

28 files changed

+615
-61
lines changed

Application/Commands/CancelPlayOfferCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
namespace PlayOfferService.Application.Commands;
44

5-
public record CancelPlayOfferCommand(Guid PlayOfferId) : IRequest<Task>
5+
public record CancelPlayOfferCommand(Guid PlayOfferId, Guid MemberId) : IRequest<Task>
66
{
77
}

Application/Commands/CreatePlayOfferCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
using PlayOfferService.Domain.Models;
33

44
namespace PlayOfferService.Application.Commands;
5-
public record CreatePlayOfferCommand(CreatePlayOfferDto CreatePlayOfferDto) : IRequest<Guid>
5+
public record CreatePlayOfferCommand(CreatePlayOfferDto CreatePlayOfferDto, Guid CreatorId, Guid ClubId) : IRequest<Guid>
66
{
77
}

Application/Commands/JoinPlayOfferCommand.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
using PlayOfferService.Domain.Models;
33

44
namespace PlayOfferService.Application.Commands;
5-
public record JoinPlayOfferCommand(JoinPlayOfferDto JoinPlayOfferDto) : IRequest<Task>
5+
public record JoinPlayOfferCommand(JoinPlayOfferDto JoinPlayOfferDto, Guid MemberId) : IRequest<Task>
66
{
77
}

Application/Controllers/PlayOfferController.cs

+48-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
using System.Security.Claims;
12
using MediatR;
3+
using Microsoft.AspNetCore.Authorization;
24
using Microsoft.AspNetCore.Mvc;
35
using PlayOfferService.Application.Commands;
6+
using PlayOfferService.Application.Exceptions;
47
using PlayOfferService.Application.Queries;
58
using PlayOfferService.Domain.Models;
69

@@ -18,22 +21,22 @@ public PlayOfferController(IMediator mediator)
1821
_mediator = mediator;
1922
}
2023

21-
///<summary>
22-
///Retrieve all Play Offers of a club with a matching id
23-
///</summary>
24-
///<param name="clubId">The id of the club of the play offer</param>
25-
///<returns>Play offers with a matching club id</returns>
26-
///<response code="200">Returns a list of Play offers matching the query params</response>
27-
///<response code="204">No Play offer with matching properties was found</response>
24+
/// <summary>
25+
/// Retrieve all Play Offers of the logged in users club
26+
/// </summary>
27+
/// <returns>Play offers with a matching club id</returns>
28+
/// <response code="200">Returns a list of Play offers matching the query params</response>
29+
/// <response code="204">No Play offer with matching properties was found</response>
2830
[HttpGet]
31+
[Authorize]
2932
[Route("club")]
3033
[ProducesResponseType(typeof(IEnumerable<PlayOfferDto>), StatusCodes.Status200OK)]
3134
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)]
3235
[Consumes("application/json")]
3336
[Produces("application/json")]
34-
public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByClubIdAsync([FromQuery] Guid clubId)
37+
public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByClubIdAsync()
3538
{
36-
//TODO: refactor after jwt implementation to get clubId from token
39+
var clubId = Guid.Parse(User.Claims.First(c => c.Type == "tennisClubId").Value);
3740
var result = await _mediator.Send(new GetPlayOffersByClubIdQuery(clubId));
3841

3942
if (result.Count() == 0)
@@ -43,21 +46,21 @@ public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByClubIdAsync([Fro
4346
}
4447

4548
///<summary>
46-
///Retrieve all Play Offers of a participating member
49+
///Retrieve all Play Offers of a logged in user
4750
///</summary>
48-
///<param name="participantId">The id of the member participating in the play offer</param>
4951
///<returns>List of Play offers with where given member is creator or opponent</returns>
5052
///<response code="200">Returns a list of Play offers matching the query params</response>
5153
///<response code="204">No Play offer with matching properties was found</response>
5254
[HttpGet]
55+
[Authorize]
5356
[Route("participant")]
5457
[ProducesResponseType(typeof(IEnumerable<PlayOffer>), StatusCodes.Status200OK)]
5558
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)]
5659
[Consumes("application/json")]
5760
[Produces("application/json")]
58-
public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByParticipantIdAsync([FromQuery] Guid participantId)
61+
public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByParticipantIdAsync()
5962
{
60-
//TODO: refactor after jwt implementation to get participantId from token
63+
var participantId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
6164
var result = await _mediator.Send(new GetPlayOffersByParticipantIdQuery(participantId));
6265

6366
if (result.Count() == 0)
@@ -74,6 +77,7 @@ public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByParticipantIdAsy
7477
///<response code="200">Returns a List of Play offers with creator matching the query params</response>
7578
///<response code="204">No Play offers with matching creator was found</response>
7679
[HttpGet]
80+
[Authorize]
7781
[Route("search")]
7882
[ProducesResponseType(typeof(IEnumerable<PlayOffer>), StatusCodes.Status200OK)]
7983
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status204NoContent)]
@@ -100,23 +104,31 @@ public async Task<ActionResult<IEnumerable<PlayOfferDto>>> GetByCreatorNameAsync
100104

101105

102106
///<summary>
103-
///Create a new Play Offer
107+
///Create a new Play Offer for the logged in user
104108
///</summary>
105109
///<param name="createPlayOfferDto">The Play Offer to create</param>
106110
///<returns>The newly created Play offer</returns>
107111
///<response code="200">Returns the id of the created Play Offer</response>
108112
///<response code="400">Invalid Play Offer structure</response>
113+
///<response code="401">Only members can create Play Offers</response>
109114
[HttpPost]
115+
[Authorize]
110116
[ProducesResponseType(typeof(PlayOffer), StatusCodes.Status201Created)]
111117
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)]
112118
[Consumes("application/json")]
113119
[Produces("application/json")]
114120
public async Task<ActionResult<PlayOffer>> Create(CreatePlayOfferDto createPlayOfferDto)
115121
{
122+
if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER")
123+
return Unauthorized("Only members can create Play Offers!");
124+
125+
var creatorId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
126+
var clubId = Guid.Parse(User.FindFirst("tennisClubId")!.Value);
127+
116128
Guid result;
117129
try
118130
{
119-
result = await _mediator.Send(new CreatePlayOfferCommand(createPlayOfferDto));
131+
result = await _mediator.Send(new CreatePlayOfferCommand(createPlayOfferDto, creatorId, clubId));
120132
}
121133
catch (Exception e)
122134
{
@@ -127,22 +139,33 @@ public async Task<ActionResult<PlayOffer>> Create(CreatePlayOfferDto createPlayO
127139
}
128140

129141
///<summary>
130-
///Cancels a Play Offer with a matching id
142+
///Cancels a Play Offer with a matching id of the logged in user
131143
///</summary>
132144
///<param name="playOfferId">The id of the Play Offer to cancel</param>
133145
///<returns>Nothing</returns>
134146
///<response code="200">The Play Offer with the matching id was cancelled</response>
135147
///<response code="400">No Play Offer with matching id found</response>
148+
///<response code="401">Only creator can cancel Play Offers</response>
136149
[HttpDelete]
150+
[Authorize]
137151
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status200OK)]
138152
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)]
139153
[Consumes("application/json")]
140154
[Produces("application/json")]
141155
public async Task<ActionResult> Delete(Guid playOfferId)
142156
{
157+
if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER")
158+
return Unauthorized("Only members can cancel Play Offers!");
159+
160+
var memberId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
161+
143162
try
144163
{
145-
await _mediator.Send(new CancelPlayOfferCommand(playOfferId));
164+
await _mediator.Send(new CancelPlayOfferCommand(playOfferId, memberId));
165+
}
166+
catch (AuthorizationException e)
167+
{
168+
return Unauthorized(e.Message);
146169
}
147170
catch (Exception e)
148171
{
@@ -153,23 +176,29 @@ public async Task<ActionResult> Delete(Guid playOfferId)
153176
}
154177

155178
///<summary>
156-
///Adds a given opponentId to a Play Offer and creates a reservation
179+
///Logged in user joins a Play Offer with a matching playOfferId
157180
///</summary>
158181
///<param name="joinPlayOfferDto">The opponentId to add to the Play Offer with the matching playOfferId</param>
159182
///<returns>Nothing</returns>
160183
///<response code="200">The opponentId was added to the Play Offer with the matching playOfferId</response>
161184
///<response code="400">No playOffer with a matching playOfferId found</response>
185+
///<response code="401">Only members can join Play Offers</response>
162186
[HttpPost]
187+
[Authorize]
163188
[Route("join")]
164189
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status200OK)]
165190
[ProducesResponseType(typeof(ActionResult), StatusCodes.Status400BadRequest)]
166191
[Consumes("application/json")]
167192
[Produces("application/json")]
168193
public async Task<ActionResult> Join(JoinPlayOfferDto joinPlayOfferDto)
169194
{
195+
if (User.Claims.First(c => c.Type == "groups").Value != "MEMBER")
196+
return Unauthorized("Only members can join Play Offers!");
197+
198+
var memberId = Guid.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
170199
try
171200
{
172-
await _mediator.Send(new JoinPlayOfferCommand(joinPlayOfferDto));
201+
await _mediator.Send(new JoinPlayOfferCommand(joinPlayOfferDto, memberId));
173202
}
174203
catch (Exception e)
175204
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace PlayOfferService.Application.Exceptions;
2+
3+
public class AuthorizationException(string message) : Exception(message);

Application/Handlers/CancelPlayOfferHandler.cs

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public async Task<Task> Handle(CancelPlayOfferCommand request, CancellationToken
2727
var existingPlayOffer = (await _playOfferRepository.GetPlayOffersByIds(request.PlayOfferId)).FirstOrDefault();
2828
if (existingPlayOffer == null)
2929
throw new NotFoundException($"PlayOffer {request.PlayOfferId} not found!");
30+
if (existingPlayOffer.CreatorId != request.MemberId)
31+
throw new AuthorizationException($"PlayOffer {request.PlayOfferId} can only be cancelled by creator!");
32+
3033
if (existingPlayOffer.OpponentId != null)
3134
throw new InvalidOperationException($"PlayOffer {request.PlayOfferId} is already accepted and cannot be cancelled!");
3235
if (existingPlayOffer.IsCancelled)

Application/Handlers/CreatePlayOfferHandler.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ public async Task<Guid> Handle(CreatePlayOfferCommand request, CancellationToken
2525
{
2626
var playOfferDto = request.CreatePlayOfferDto;
2727

28-
var club = await _clubRepository.GetClubById(playOfferDto.ClubId);
28+
var club = await _clubRepository.GetClubById(request.ClubId);
2929
if(club == null)
30-
throw new NotFoundException($"Club {request.CreatePlayOfferDto.ClubId} not found");
30+
throw new NotFoundException($"Club {request.ClubId} not found");
3131
switch (club.Status)
3232
{
3333
case Status.LOCKED:
@@ -36,9 +36,9 @@ public async Task<Guid> Handle(CreatePlayOfferCommand request, CancellationToken
3636
throw new InvalidOperationException("Can't create PlayOffer in deleted club!");
3737
}
3838

39-
var creator = await _memberRepository.GetMemberById(playOfferDto.CreatorId);
39+
var creator = await _memberRepository.GetMemberById(request.CreatorId);
4040
if(creator == null)
41-
throw new NotFoundException($"Member {request.CreatePlayOfferDto.CreatorId} not found!");
41+
throw new NotFoundException($"Member {request.CreatorId} not found!");
4242
switch (creator.Status)
4343
{
4444
case Status.LOCKED:

Application/Handlers/GetPlayOffersByClubIdHandler.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ public async Task<IEnumerable<PlayOfferDto>> Handle(GetPlayOffersByClubIdQuery r
2828
var clubDto = new ClubDto((await _clubRepository.GetClubById(request.ClubId))!);
2929
var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList();
3030
var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList();
31-
var reservationDtos = (await _reservationRepository.GetAllReservations()).Select(reservation => new ReservationDto(reservation, courtDtos)).ToList();
31+
var reservationDtos = (await _reservationRepository.GetAllReservations())
32+
.Select(reservation => new ReservationDto(
33+
reservation,
34+
courtDtos.First(courtDto => courtDto.Id == reservation.CourtId)))
35+
.ToList();
3236

3337
var playOfferDtos = new List<PlayOfferDto>();
3438
foreach (var playOffer in playOffers)

Application/Handlers/GetPlayOffersByCreatorNameHandler.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ public async Task<IEnumerable<PlayOfferDto>> Handle(GetPlayOffersByCreatorNameQu
3838
var clubDto = (await _clubRepository.GetAllClubs()).Select(club => new ClubDto(club)).ToList();
3939
var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList();
4040
var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList();
41-
var reservationDtos = (await _reservationRepository.GetAllReservations()).Select(reservation => new ReservationDto(reservation, courtDtos)).ToList();
41+
var reservationDtos = (await _reservationRepository.GetAllReservations())
42+
.Select(reservation => new ReservationDto(
43+
reservation,
44+
courtDtos.First(courtDto => courtDto.Id == reservation.CourtId)))
45+
.ToList();
4246

4347
var playOfferDtos = new List<PlayOfferDto>();
4448
foreach (var playOffer in playOffers)

Application/Handlers/GetPlayOffersByParticipantIdHandler.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ public async Task<IEnumerable<PlayOfferDto>> Handle(GetPlayOffersByParticipantId
2828
var clubDto = (await _clubRepository.GetAllClubs()).Select(club => new ClubDto(club)).ToList();
2929
var memberDtos = (await _memberRepository.GetAllMembers()).Select(member => new MemberDto(member)).ToList();
3030
var courtDtos = (await _courtRepository.GetAllCourts()).Select(court => new CourtDto(court)).ToList();
31-
var reservationDtos = (await _reservationRepository.GetAllReservations()).Select(reservation => new ReservationDto(reservation, courtDtos)).ToList();
31+
var reservationDtos = (await _reservationRepository.GetAllReservations())
32+
.Select(reservation => new ReservationDto(
33+
reservation,
34+
courtDtos.First(courtDto => courtDto.Id == reservation.CourtId)))
35+
.ToList();
3236

3337
var playOfferDtos = new List<PlayOfferDto>();
3438
foreach (var playOffer in playOffers)

Application/Handlers/JoinPlayOfferHandler.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public async Task<Task> Handle(JoinPlayOfferCommand request, CancellationToken c
3030
if (existingPlayOffer == null)
3131
throw new NotFoundException($"PlayOffer {request.JoinPlayOfferDto.PlayOfferId} not found!");
3232

33-
var existingOpponent = await _memberRepository.GetMemberById(request.JoinPlayOfferDto.OpponentId);
33+
var existingOpponent = await _memberRepository.GetMemberById(request.MemberId);
3434
if (existingOpponent == null)
35-
throw new NotFoundException($"Member {request.JoinPlayOfferDto.OpponentId} not found!");
35+
throw new NotFoundException($"Member {request.MemberId} not found!");
3636

3737
if (existingOpponent.Id == existingPlayOffer.CreatorId)
3838
throw new InvalidOperationException("Can't join your own PlayOffer!");

Domain/DbReadContext.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
2424
modelBuilder.ApplyConfiguration(new BaseEventConfiguration());
2525

2626
// TODO: Remove before coop testing
27-
var testClub = new Club { Id = Guid.Parse("06b812a7-5131-4510-82ff-bffac33e0f3e"), Name = "Test Club", Status = Status.ACTIVE };
28-
var testMemberIds = new List<Guid> { Guid.Parse("40c0981d-e2f8-4af3-ae6c-17f79f3ba8c2"), Guid.Parse("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d") };
27+
var testClub = new Club { Id = Guid.Parse("1fc64a89-9e63-4e9f-96f7-e2120f0ca6c3"), Name = "Test Club", Status = Status.ACTIVE };
28+
var testMemberIds = new List<Guid> { Guid.Parse("0033506d-2d59-40d3-b996-74a251091027"), Guid.Parse("ccc1c8fc-89b5-4026-b190-9d9e7e7bc18d") };
2929

3030
var testMembers = new List<Object>{
3131
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)
3737
{
3838
Id = Guid.NewGuid(),
3939
ClubId = testClub.Id,
40-
CreatorId = testMemberIds[0],
40+
CreatorId = testMemberIds[1],
4141
ProposedStartTime = DateTime.UtcNow,
4242
ProposedEndTime = DateTime.UtcNow.AddHours(1),
4343
IsCancelled = false

Domain/Events/Reservation/ReservationCreatedEvent.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class ReservationCreatedEvent : DomainEvent
1515
[JsonPropertyName("participantIds")]
1616
public List<Guid>? ParticipantIds { get; set; }
1717
[JsonPropertyName("courtIds")]
18-
public List<Guid> CourtIds { get; set; }
18+
public Guid CourtId { get; set; }
1919

2020
public ReservationCreatedEvent(){}
2121
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
using System.Text.Json.Serialization;
2+
13
namespace PlayOfferService.Domain.Events.Reservation;
24

35
public class ReservationLimitExceededEvent : DomainEvent
46
{
7+
[JsonPropertyName("start")]
8+
public DateTime Start { get; set; }
9+
[JsonPropertyName("end")]
10+
public DateTime End { get; set; }
11+
[JsonPropertyName("reservantId")]
12+
public Guid? ReservantId { get; set; }
13+
[JsonPropertyName("tournamentId")]
14+
public Guid? TournamentId { get; set; }
15+
[JsonPropertyName("participantIds")]
16+
public List<Guid>? ParticipantIds { get; set; }
17+
[JsonPropertyName("courtIds")]
18+
public Guid CourtId { get; set; }
19+
20+
public ReservationLimitExceededEvent(){}
521
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1+
using System.Text.Json.Serialization;
12
using MediatR;
23

34
namespace PlayOfferService.Domain.Events.Reservation;
45

56
public class ReservationRejectedEvent : DomainEvent
67
{
8+
[JsonPropertyName("start")]
9+
public DateTime Start { get; set; }
10+
[JsonPropertyName("end")]
11+
public DateTime End { get; set; }
12+
[JsonPropertyName("reservantId")]
13+
public Guid? ReservantId { get; set; }
14+
[JsonPropertyName("tournamentId")]
15+
public Guid? TournamentId { get; set; }
16+
[JsonPropertyName("participantIds")]
17+
public List<Guid>? ParticipantIds { get; set; }
18+
[JsonPropertyName("courtIds")]
19+
public Guid CourtId { get; set; }
20+
21+
public ReservationRejectedEvent(){}
722
}

Domain/Models/DTOs/CreatePlayOfferDto.cs

-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ namespace PlayOfferService.Domain.Models;
22

33
public class CreatePlayOfferDto
44
{
5-
public Guid ClubId { get; set; }
6-
public Guid CreatorId { get; set; }
75
public DateTime ProposedStartTime { get; set; }
86
public DateTime ProposedEndTime { get; set; }
97
}

Domain/Models/DTOs/ReservationDto.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ namespace PlayOfferService.Domain.Models;
33
public class ReservationDto
44
{
55
public Guid Id { get; set; }
6-
public List<CourtDto> Courts { get; set; }
6+
public CourtDto Court { get; set; }
77
public DateTime StartTime { get; set; }
88
public DateTime EndTime { get; set; }
99
public bool IsCancelled { get; set; }
1010

11-
public ReservationDto(Reservation reservation, List<CourtDto> courts)
11+
public ReservationDto(Reservation reservation, CourtDto court)
1212
{
1313
Id = reservation.Id;
14-
Courts = courts.Where(c => reservation.CourtIds.Contains(c.Id)).ToList();
14+
Court = court;
1515
StartTime = reservation.StartTime;
1616
EndTime = reservation.EndTime;
1717
IsCancelled = reservation.IsCancelled;

0 commit comments

Comments
 (0)