From 67cc1c2d5e59194b4d57bf01d6f954fb42a5d865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schmidt?= <9435005+AnSch1510@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:00:30 +0200 Subject: [PATCH 1/5] feat: admin can add player to timeslot --- .../event-timeslot.component.html | 62 +++++- .../event-timeslot.component.ts | 85 +++++++- src/client/src/app/i18n/de.json | 14 +- src/client/src/app/i18n/en.json | 14 +- .../PatchPlayerEventRegistrationsEndpoint.cs | 190 ++++++++++++++++++ 5 files changed, 349 insertions(+), 16 deletions(-) create mode 100644 src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs diff --git a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.html b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.html index 9231df0..cb44d70 100644 --- a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.html +++ b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.html @@ -186,21 +186,63 @@

} -

- {{ translations.events_timeslot_registeredPlayers() }} ({{ timeslot.playerIds.length }}) -

+
+

+ {{ translations.events_timeslot_registeredPlayers() }} +

+ + {{ timeslot.playerIds.length }} + + @if (!event.startedAt) { + + + } +
+
@for (player of players(); track player.id; let index = $index) {
- @if (player.alias) { - {{ player.alias }} - } @else { - <{{ translations.events_timeslot_unknownPlayer() }}> - ({{ player.id }}) - } +
+ @if (player.alias) { + {{ player.alias }} + } @else { + <{{ translations.events_timeslot_unknownPlayer() }}> + ({{ player.id }}) + } +
+
} @if (timeslot.playerIds.length === 0) { diff --git a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts index e98ac9d..a26e1e4 100644 --- a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts +++ b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts @@ -6,11 +6,13 @@ import { Router, ActivatedRoute } from '@angular/router'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { AccordionModule } from 'primeng/accordion'; -import { ConfirmationService } from 'primeng/api'; +import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { CardModule } from 'primeng/card'; import { DropdownModule } from 'primeng/dropdown'; +import { ListboxModule } from 'primeng/listbox'; import { MessagesModule } from 'primeng/messages'; +import { OverlayPanelModule } from 'primeng/overlaypanel'; import { ProgressSpinnerModule } from 'primeng/progressspinner'; import { TooltipModule } from 'primeng/tooltip'; import { map } from 'rxjs'; @@ -35,17 +37,27 @@ import { selectUsersActionState, userSelectors, } from '../../../+state/users'; -import { EventInstancePreconfiguration, User } from '../../../models/parsed-models'; +import { EventsService } from '../../../api/services'; +import { + EventInstance, + EventInstancePreconfiguration, + EventTimeslot, + User, +} from '../../../models/parsed-models'; import { TranslateService } from '../../../services/translate.service'; -import { ifTruthy, isNullish } from '../../../utils/common.utils'; +import { ifTruthy, isNullish, notNullish } from '../../../utils/common.utils'; import { dateWithTime, timeToString } from '../../../utils/date.utils'; import { errorToastEffect, selectSignal } from '../../../utils/ngrx.utils'; +import { UserItemComponent } from '../../users/user-item/user-item.component'; import { EventTimeslotDialogComponent } from '../event-timeslot-dialog/event-timeslot-dialog.component'; function asString(value: unknown): string | null { return typeof value === 'string' ? value : null; } +type EventInstances = { timeslot: EventTimeslot; instances: EventInstance[] }[]; +type EventPlayer = Partial & { id: string }; + @Component({ selector: 'app-event-timeslot', standalone: true, @@ -57,9 +69,12 @@ function asString(value: unknown): string | null { DropdownModule, EventTimeslotDialogComponent, FormsModule, + ListboxModule, MessagesModule, + OverlayPanelModule, ProgressSpinnerModule, TooltipModule, + UserItemComponent, ], templateUrl: './event-timeslot.component.html', styleUrl: './event-timeslot.component.scss', @@ -71,6 +86,8 @@ export class EventTimeslotComponent { private readonly _activatedRoute = inject(ActivatedRoute); private readonly _translateService = inject(TranslateService); private readonly _confirmationService = inject(ConfirmationService); + private readonly _eventService = inject(EventsService); + private readonly _messageService = inject(MessageService); protected readonly translations = this._translateService.translations; protected readonly locale = this._translateService.language; @@ -110,11 +127,16 @@ export class EventTimeslotComponent { this.timeslot(), timeslot => timeslot.playerIds - .map & { id: string }>(x => this.allUsers()[x] ?? { id: x }) + .map(x => this.allUsers()[x] ?? { id: x }) .sort((a, b) => (a?.alias ?? '').localeCompare(b?.alias ?? '')), [] ) ); + protected readonly availablePlayers = computed(() => + Object.values(this.allUsers()) + .filter(notNullish) + .filter(u => !this.players().some(p => p.id === u.id)) + ); protected readonly preconfigPlayerOptions = computed(() => this.players().filter( x => !this.timeslot()?.preconfigurations.some(p => p.playerIds.includes(x.id)) @@ -168,6 +190,61 @@ export class EventTimeslotComponent { } } + protected async addPlayer(player: User) { + const eventId = this.eventId(); + const timeslotId = this.timeslotId(); + + if (isNullish(eventId) || isNullish(timeslotId)) return; + + const response = await this._eventService.patchPlayerEventRegistrations({ + eventId, + body: { userId: player.id, timeslotId: timeslotId, isRegistered: true }, + }); + if (response.ok) { + this._messageService.add({ + severity: 'success', + summary: this.translations.events_timeslot_playerAdded(), + life: 2000, + }); + } + } + + protected async removeUser(user: EventPlayer) { + const eventId = this.eventId(); + const timeslotId = this.timeslotId(); + + if (isNullish(eventId) || isNullish(timeslotId)) return; + + this._confirmationService.confirm({ + header: this.translations.events_timeslot_playerRemoveDialog_header(user), + message: this.translations.events_timeslot_playerRemoveDialog_message(), + acceptLabel: this.translations.shared_delete(), + acceptButtonStyleClass: 'p-button-danger', + acceptIcon: 'p-button-icon-left i-[mdi--delete]', + rejectLabel: this.translations.shared_cancel(), + rejectButtonStyleClass: 'p-button-text', + accept: async () => { + const response = await this._eventService.patchPlayerEventRegistrations({ + eventId, + body: { userId: user.id, timeslotId: timeslotId, isRegistered: false }, + }); + if (response.ok) { + this._messageService.add({ + severity: 'success', + summary: this.translations.events_timeslot_playerRemoveDialog_playerRemoved(), + life: 2000, + }); + } else { + this._messageService.add({ + severity: 'error', + summary: this.translations.events_timeslot_playerRemoveDialog_error_playerRemoved(), + life: 2000, + }); + } + }, + }); + } + protected addPreconfig() { const eventId = this.eventId(); const timeslotId = this.timeslotId(); diff --git a/src/client/src/app/i18n/de.json b/src/client/src/app/i18n/de.json index 1722956..571c308 100644 --- a/src/client/src/app/i18n/de.json +++ b/src/client/src/app/i18n/de.json @@ -101,7 +101,19 @@ "time": "Startzeit", "mapToPlay": "Zu spielende Bahn", "map": "Bahn", - "isFallbackAllowed": "Spieler können einen Ausweichzeitslot festlegen" + "isFallbackAllowed": "Spieler können einen Ausweichzeitslot festlegen", + "playerAdded": "Spieler wurde diesem Zeitslot hinzugefügt", + "playerRemoveDialog": { + "header": "Spieler \"{{alias}}\" entfernen?", + "message": "Möchtest du den Spieler wirklich aus dem Zeitslot entfernen?", + "playerRemoved": "Spieler wurde aus diesem Zeitslot entfernt", + "error": { + "playerRemoved": "Spieler wurde aus diesem Zeitslot nicht entfernt" + } + }, + "error": { + "playerAdded": "Spieler wurde diesem Zeitslot nicht hinzugefügt" + } }, "createDialog": { "title": "Veranstaltung erstellen", diff --git a/src/client/src/app/i18n/en.json b/src/client/src/app/i18n/en.json index ca65307..b6d4c47 100644 --- a/src/client/src/app/i18n/en.json +++ b/src/client/src/app/i18n/en.json @@ -101,7 +101,19 @@ "time": "Start time", "mapToPlay": "Map to be played", "map": "Map", - "isFallbackAllowed": "Players can define a fallback timeslot" + "isFallbackAllowed": "Players can define a fallback timeslot", + "playerAdded": "Player has been added to this timeslot", + "playerRemoveDialog": { + "header": "Remove Player \"{{alias}}\"", + "message": "Do you really want to remove the player from this timeslot?", + "playerRemoved": "Player has been removed from this timeslot", + "error": { + "playerRemoved": "Player has not been removed from this timeslot" + } + }, + "error": { + "playerAdded": "Player has not been added to this timeslot" + } }, "createDialog": { "title": "Create event", diff --git a/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs new file mode 100644 index 0000000..782a3ef --- /dev/null +++ b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs @@ -0,0 +1,190 @@ +using System.ComponentModel.DataAnnotations; +using FastEndpoints; +using FluentValidation; +using MaSch.Core.Extensions; +using Microsoft.AspNetCore.Server.HttpSys; +using Microsoft.EntityFrameworkCore; +using MinigolfFriday.Data; +using MinigolfFriday.Data.Entities; +using MinigolfFriday.Domain.Models; +using MinigolfFriday.Domain.Models.RealtimeEvents; +using MinigolfFriday.Host.Common; +using MinigolfFriday.Host.Services; + +namespace MinigolfFriday.Host.Endpoints.Events; + +/// The id of the event to change registration. +/// The registration to change. +/// The registration state to change the timeslot to. +/// The userId to patch. Only available as Admin. +public record PatchPlayerEventRegistrationsRequest( + [property: Required] string EventId, + [property: Required] string TimeslotId, + [property: Required] bool IsRegistered, + [property: Required] string UserId +); + +public class PatchPlayerEventRegistrationsRequestValidator + : Validator +{ + public PatchPlayerEventRegistrationsRequestValidator(IIdService idService) + { + RuleFor(x => x.EventId).NotEmpty().ValidSqid(idService.Event); + RuleFor(x => x.TimeslotId).NotEmpty().ValidSqid(idService.EventTimeslot); + } +} + +public class PatchPlayerEventRegistrationsEndpoint( + DatabaseContext databaseContext, + IRealtimeEventsService realtimeEventsService, + IIdService idService, + IJwtService jwtService +) : Endpoint +{ + public override void Configure() + { + Patch("{eventId}/registrations"); + Group(); + this.ProducesErrors( + EndpointErrors.UserIdNotInClaims, + EndpointErrors.EventNotFound, + EndpointErrors.EventRegistrationElapsed, + EndpointErrors.EventAlreadyStarted, + EndpointErrors.UserNotFound + ); + } + + public override async Task HandleAsync( + PatchPlayerEventRegistrationsRequest req, + CancellationToken ct + ) + { + if (!jwtService.TryGetUserId(User, out var userId)) + { + Logger.LogWarning(EndpointErrors.UserIdNotInClaims); + await this.SendErrorAsync(EndpointErrors.UserIdNotInClaims, ct); + return; + } + if (!jwtService.HasRole(User, Role.Admin)) + { + return; + } + userId = idService.User.DecodeSingle(req.UserId); + + var user = await databaseContext.Users.FirstOrDefaultAsync(x => x.Id == userId, ct); + if (user == null) + { + Logger.LogWarning(EndpointErrors.UserNotFound, userId); + await this.SendErrorAsync( + EndpointErrors.UserNotFound, + idService.User.Encode(userId), + ct + ); + return; + } + + var eventId = idService.Event.DecodeSingle(req.EventId); + var eventInfo = await databaseContext + .Events.Where(x => x.Id == eventId) + .Select(x => new { Started = x.StartedAt != null, x.RegistrationDeadline }) + .FirstOrDefaultAsync(ct); + + if (eventInfo == null) + { + Logger.LogWarning(EndpointErrors.EventNotFound, eventId); + await this.SendErrorAsync(EndpointErrors.EventNotFound, req.EventId, ct); + return; + } + + if (eventInfo.RegistrationDeadline < DateTimeOffset.Now) + { + Logger.LogWarning( + EndpointErrors.EventRegistrationElapsed, + eventId, + eventInfo.RegistrationDeadline + ); + await this.SendErrorAsync( + EndpointErrors.EventRegistrationElapsed, + req.EventId, + eventInfo.RegistrationDeadline, + ct + ); + return; + } + + if (eventInfo.Started) + { + Logger.LogWarning(EndpointErrors.EventAlreadyStarted, eventId); + await this.SendErrorAsync(EndpointErrors.EventAlreadyStarted, req.EventId, ct); + return; + } + + var registrations = await databaseContext + .EventTimeslotRegistrations.Where(x => + x.Player.Id == userId && x.EventTimeslot.EventId == eventId + ) + .ToArrayAsync(ct); + var targetRegistration = new + { + TimeslotId = idService.EventTimeslot.DecodeSingle(req.TimeslotId) + }; + var timeslotToModify = registrations.FirstOrDefault(x => + x.EventTimeslotId == targetRegistration.TimeslotId + ); + if (timeslotToModify == null && req.IsRegistered) + { + // not existent but wants to -> then add + databaseContext.EventTimeslotRegistrations.Add( + new EventTimeslotRegistrationEntity + { + EventTimeslot = databaseContext.EventTimeslotById( + targetRegistration.TimeslotId + ), + Player = databaseContext.UserById(userId), + FallbackEventTimeslot = null + } + ); + } + else if (timeslotToModify != null && !req.IsRegistered) + { + // is existent but does not want to -> remove + databaseContext.EventTimeslotRegistrations.RemoveRange( + registrations.Where(x => targetRegistration.TimeslotId == x.EventTimeslotId) + ); + } + await databaseContext.SaveChangesAsync(ct); + await SendAsync(null, cancellation: ct); + + await realtimeEventsService.SendEventAsync( + new RealtimeEvent.PlayerEventRegistrationChanged( + idService.User.Encode(userId), + idService.Event.Encode(eventId) + ), + ct + ); + var userAlias = await databaseContext + .Users.Where(x => x.Id == userId) + .Select(x => x.Alias) + .FirstOrDefaultAsync(ct); + var changeEvents = registrations + .Where(x => targetRegistration.TimeslotId == x.EventTimeslotId) + .Select(x => new RealtimeEvent.PlayerEventTimeslotRegistrationChanged( + idService.Event.Encode(eventId), + idService.EventTimeslot.Encode(x.EventTimeslotId), + idService.User.Encode(userId), + userAlias, + req.IsRegistered + )) + .Concat( + new RealtimeEvent.PlayerEventTimeslotRegistrationChanged( + idService.Event.Encode(eventId), + idService.EventTimeslot.Encode(targetRegistration.TimeslotId), + idService.User.Encode(userId), + userAlias, + req.IsRegistered + ) + ); + foreach (var changeEvent in changeEvents) + await realtimeEventsService.SendEventAsync(changeEvent, ct); + } +} From d316c9a6e7b751c70916a0605a661d01845ea9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schmidt?= <9435005+AnSch1510@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:04:21 +0200 Subject: [PATCH 2/5] refactor sendNotificationEndpoint --- .../Endpoints/Notifications/SendNotificationEndpoint.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/server/host/Endpoints/Notifications/SendNotificationEndpoint.cs b/src/server/host/Endpoints/Notifications/SendNotificationEndpoint.cs index 625195c..a951bf1 100644 --- a/src/server/host/Endpoints/Notifications/SendNotificationEndpoint.cs +++ b/src/server/host/Endpoints/Notifications/SendNotificationEndpoint.cs @@ -48,14 +48,12 @@ public override async Task HandleAsync(SendNotificationRequest req, Cancellation await this.SendErrorAsync(EndpointErrors.UserIdNotInClaims, ct); return; } - if (!req.UserId.IsNullOrEmpty() && jwtService.HasRole(User, Role.Admin)) + if (req.UserId != null && jwtService.HasRole(User, Role.Admin)) { userId = idService.User.DecodeSingle(req.UserId); } - var user = await databaseContext - .Users.Include(x => x.Settings) - .FirstOrDefaultAsync(x => x.Id == userId, ct); + var user = await databaseContext.Users.FirstOrDefaultAsync(x => x.Id == userId, ct); if (user == null) { From 98b674a6a6be8d1c70610da888bc40a581971d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schmidt?= <9435005+AnSch1510@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:16:49 +0200 Subject: [PATCH 3/5] fix add not working issue --- .../events/event-timeslot/event-timeslot.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts index a26e1e4..c82af66 100644 --- a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts +++ b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts @@ -190,7 +190,7 @@ export class EventTimeslotComponent { } } - protected async addPlayer(player: User) { + protected async addPlayer(userId: string) { const eventId = this.eventId(); const timeslotId = this.timeslotId(); @@ -198,7 +198,7 @@ export class EventTimeslotComponent { const response = await this._eventService.patchPlayerEventRegistrations({ eventId, - body: { userId: player.id, timeslotId: timeslotId, isRegistered: true }, + body: { userId: userId, timeslotId: timeslotId, isRegistered: true }, }); if (response.ok) { this._messageService.add({ @@ -206,6 +206,12 @@ export class EventTimeslotComponent { summary: this.translations.events_timeslot_playerAdded(), life: 2000, }); + } else { + this._messageService.add({ + severity: 'error', + summary: this.translations.events_timeslot_error_playerAdded(), + life: 2000, + }); } } From 84d57803363e1c70263960cd5ab7a327cc586af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schmidt?= <9435005+AnSch1510@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:38:36 +0200 Subject: [PATCH 4/5] apply suggestions --- .../event-timeslot.component.ts | 9 +--- src/client/src/app/i18n/de.json | 4 +- src/client/src/app/i18n/en.json | 4 +- .../Events/EventsAdministrationGroup.cs | 19 +++++++++ .../PatchPlayerEventRegistrationsEndpoint.cs | 41 ++++--------------- 5 files changed, 33 insertions(+), 44 deletions(-) create mode 100644 src/server/host/Endpoints/Events/EventsAdministrationGroup.cs diff --git a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts index c82af66..0f1f3f1 100644 --- a/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts +++ b/src/client/src/app/components/events/event-timeslot/event-timeslot.component.ts @@ -38,12 +38,7 @@ import { userSelectors, } from '../../../+state/users'; import { EventsService } from '../../../api/services'; -import { - EventInstance, - EventInstancePreconfiguration, - EventTimeslot, - User, -} from '../../../models/parsed-models'; +import { EventInstancePreconfiguration, User } from '../../../models/parsed-models'; import { TranslateService } from '../../../services/translate.service'; import { ifTruthy, isNullish, notNullish } from '../../../utils/common.utils'; import { dateWithTime, timeToString } from '../../../utils/date.utils'; @@ -54,8 +49,6 @@ import { EventTimeslotDialogComponent } from '../event-timeslot-dialog/event-tim function asString(value: unknown): string | null { return typeof value === 'string' ? value : null; } - -type EventInstances = { timeslot: EventTimeslot; instances: EventInstance[] }[]; type EventPlayer = Partial & { id: string }; @Component({ diff --git a/src/client/src/app/i18n/de.json b/src/client/src/app/i18n/de.json index 571c308..c674efb 100644 --- a/src/client/src/app/i18n/de.json +++ b/src/client/src/app/i18n/de.json @@ -108,11 +108,11 @@ "message": "Möchtest du den Spieler wirklich aus dem Zeitslot entfernen?", "playerRemoved": "Spieler wurde aus diesem Zeitslot entfernt", "error": { - "playerRemoved": "Spieler wurde aus diesem Zeitslot nicht entfernt" + "playerRemoved": "Fehler beim entfernen aufgetreten." } }, "error": { - "playerAdded": "Spieler wurde diesem Zeitslot nicht hinzugefügt" + "playerAdded": "Fehler beim hinzufpgen aufgetreten." } }, "createDialog": { diff --git a/src/client/src/app/i18n/en.json b/src/client/src/app/i18n/en.json index b6d4c47..43f4488 100644 --- a/src/client/src/app/i18n/en.json +++ b/src/client/src/app/i18n/en.json @@ -108,11 +108,11 @@ "message": "Do you really want to remove the player from this timeslot?", "playerRemoved": "Player has been removed from this timeslot", "error": { - "playerRemoved": "Player has not been removed from this timeslot" + "playerRemoved": "Error while removing Ppayer from timeslot" } }, "error": { - "playerAdded": "Player has not been added to this timeslot" + "playerAdded": "Error while adding player to timeslot" } }, "createDialog": { diff --git a/src/server/host/Endpoints/Events/EventsAdministrationGroup.cs b/src/server/host/Endpoints/Events/EventsAdministrationGroup.cs new file mode 100644 index 0000000..270f922 --- /dev/null +++ b/src/server/host/Endpoints/Events/EventsAdministrationGroup.cs @@ -0,0 +1,19 @@ +using FastEndpoints; +using MinigolfFriday.Domain.Models; + +namespace MinigolfFriday.Host.Endpoints.Events; + +public class EventsAdministrationGroup : Group +{ + public EventsAdministrationGroup() + { + Configure( + "events", + x => + { + x.Roles(nameof(Role.Admin)); + x.Description(x => x.WithTags("Events")); + } + ); + } +} diff --git a/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs index 782a3ef..df6a50d 100644 --- a/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs +++ b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs @@ -2,11 +2,9 @@ using FastEndpoints; using FluentValidation; using MaSch.Core.Extensions; -using Microsoft.AspNetCore.Server.HttpSys; using Microsoft.EntityFrameworkCore; using MinigolfFriday.Data; using MinigolfFriday.Data.Entities; -using MinigolfFriday.Domain.Models; using MinigolfFriday.Domain.Models.RealtimeEvents; using MinigolfFriday.Host.Common; using MinigolfFriday.Host.Services; @@ -37,14 +35,13 @@ public PatchPlayerEventRegistrationsRequestValidator(IIdService idService) public class PatchPlayerEventRegistrationsEndpoint( DatabaseContext databaseContext, IRealtimeEventsService realtimeEventsService, - IIdService idService, - IJwtService jwtService + IIdService idService ) : Endpoint { public override void Configure() { Patch("{eventId}/registrations"); - Group(); + Group(); this.ProducesErrors( EndpointErrors.UserIdNotInClaims, EndpointErrors.EventNotFound, @@ -59,17 +56,7 @@ public override async Task HandleAsync( CancellationToken ct ) { - if (!jwtService.TryGetUserId(User, out var userId)) - { - Logger.LogWarning(EndpointErrors.UserIdNotInClaims); - await this.SendErrorAsync(EndpointErrors.UserIdNotInClaims, ct); - return; - } - if (!jwtService.HasRole(User, Role.Admin)) - { - return; - } - userId = idService.User.DecodeSingle(req.UserId); + var userId = idService.User.DecodeSingle(req.UserId); var user = await databaseContext.Users.FirstOrDefaultAsync(x => x.Id == userId, ct); if (user == null) @@ -166,25 +153,15 @@ await realtimeEventsService.SendEventAsync( .Users.Where(x => x.Id == userId) .Select(x => x.Alias) .FirstOrDefaultAsync(ct); - var changeEvents = registrations - .Where(x => targetRegistration.TimeslotId == x.EventTimeslotId) - .Select(x => new RealtimeEvent.PlayerEventTimeslotRegistrationChanged( + await realtimeEventsService.SendEventAsync( + new RealtimeEvent.PlayerEventTimeslotRegistrationChanged( idService.Event.Encode(eventId), - idService.EventTimeslot.Encode(x.EventTimeslotId), + idService.EventTimeslot.Encode(targetRegistration.TimeslotId), idService.User.Encode(userId), userAlias, req.IsRegistered - )) - .Concat( - new RealtimeEvent.PlayerEventTimeslotRegistrationChanged( - idService.Event.Encode(eventId), - idService.EventTimeslot.Encode(targetRegistration.TimeslotId), - idService.User.Encode(userId), - userAlias, - req.IsRegistered - ) - ); - foreach (var changeEvent in changeEvents) - await realtimeEventsService.SendEventAsync(changeEvent, ct); + ), + ct + ); } } From 23facfdbd68547a19954a83c03a21bb44941eb7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schmidt?= <9435005+AnSch1510@users.noreply.github.com> Date: Wed, 31 Jul 2024 20:39:04 +0200 Subject: [PATCH 5/5] be able to add/remove player to/from a timeslot even if the event has already started --- .../PatchPlayerEventRegistrationsEndpoint.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs index df6a50d..b2a0d71 100644 --- a/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs +++ b/src/server/host/Endpoints/Events/PatchPlayerEventRegistrationsEndpoint.cs @@ -83,29 +83,6 @@ await this.SendErrorAsync( return; } - if (eventInfo.RegistrationDeadline < DateTimeOffset.Now) - { - Logger.LogWarning( - EndpointErrors.EventRegistrationElapsed, - eventId, - eventInfo.RegistrationDeadline - ); - await this.SendErrorAsync( - EndpointErrors.EventRegistrationElapsed, - req.EventId, - eventInfo.RegistrationDeadline, - ct - ); - return; - } - - if (eventInfo.Started) - { - Logger.LogWarning(EndpointErrors.EventAlreadyStarted, eventId); - await this.SendErrorAsync(EndpointErrors.EventAlreadyStarted, req.EventId, ct); - return; - } - var registrations = await databaseContext .EventTimeslotRegistrations.Where(x => x.Player.Id == userId && x.EventTimeslot.EventId == eventId