fix: offload building DTOs to GUHF

DTO building allows for fully returning correct event's
skills and registrations
This commit is contained in:
2025-06-01 20:33:58 +02:00
parent 426288d728
commit efb71b24d3
10 changed files with 182 additions and 72 deletions

View File

@@ -1,18 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using WebApp.Entities;
namespace WebApp.DTOs;
// Output values in JSON file
public record class EventDetailsDto
(
int EventId,
[Required] int? OrganisationId,
[Required] string? OrganisationName,
[Required][StringLength(50)] string Title,
[StringLength(500)] string Description,
[Required][StringLength(100)] string Location,
[Required] DateTime? EventDate,
ICollection<EventSkill> EventSkills,
ICollection<EventRegistration> EventRegistrations
);
{
public int EventId { get; set; }
[Required] public int? OrganisationId { get; set; }
[Required] public string? OrganisationName { get; set; }
[Required][StringLength(50)] public string Title { get; set; }
[StringLength(500)] public string Description { get; set; }
[Required][StringLength(100)] public string Location { get; set; }
[Required] public DateTime? EventDate { get; set; }
//ICollection<EventSkill> EventSkills,
public ICollection<SkillSummaryDto> EventSkills { get; set; }
public ICollection<EventRegistrationDto> EventRegistrations { get; set; }
public EventDetailsDto() { }
};

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using WebApp.Entities;
namespace WebApp.DTOs;
public record class EventRegistrationDto
{
public int EventId { get; set; }
public int UserId { get; set; }
public string UserName { get; set; }
public DateTime RegisteredAt { get; set; }
public EventRegistrationDto() { }
};

View File

@@ -1,17 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using WebApp.Entities;
namespace WebApp.DTOs;
// Output values in JSON file
public record class EventSummaryDto(
int EventId,
[Required] string Organisation,
[Required] int OrganisationId,
[Required] [StringLength(50)] string Title,
[StringLength(500)] string Description,
[Required] [StringLength(100)] string Location,
[Required] DateTime? EventDate,
ICollection<EventSkill> EventSkills,
ICollection<EventRegistration> EventRegistrations
);
public record class EventSummaryDto {
public int EventId { get; set; }
[Required] public string Organisation { get; set; }
[Required] public int OrganisationId { get; set; }
[Required] [StringLength(50)] public string Title { get; set; }
[StringLength(500)] public string Description { get; set; }
[Required] [StringLength(100)] public string Location { get; set; }
[Required] public DateTime? EventDate { get; set; }
public ICollection<EventSkill> EventSkills { get; set; }
public ICollection<EventRegistration> EventRegistrations { get; set; }
};

View File

@@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using WebApp.Entities;
namespace WebApp.DTOs;
@@ -13,4 +13,5 @@ public record class EventSummaryNoErDto(
[Required][StringLength(100)] string Location,
[Required] DateTime? EventDate,
ICollection<EventSkill> EventSkills
// ICollection<SkillSummaryDto> EventSkills
);

View File

@@ -1,9 +1,13 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using WebApp.Entities;
namespace WebApp.DTOs;
public record class SkillSummaryDto
(
[Required] int SkillId,
[Required] string SkillName
);
{
public int? SkillId { get; set; }
public string? SkillName { get; set; }
public SkillSummaryDto() { }
};

View File

@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Intrinsics.Arm;
using WebApp.Data;
using WebApp.DTOs;
using WebApp.Entities;
@@ -46,8 +48,11 @@ namespace WebApp.Endpoints
group.MapGet("/{id}",
async (int id, ApplicationDbContext dbContext, HttpContext httpContext, GeneralUseHelpers guhf) =>
{
Event? Eve = await dbContext.Events.FindAsync(id);
Event? Eve = await dbContext
.Events
.Include(e => e.Organisation)
.FirstOrDefaultAsync(e => e.EventId == id);
if (Eve is null) return Results.NotFound();
// Sprawdź, czy token należy do organizacji, a jeżeli tak, to do której.
@@ -55,16 +60,14 @@ namespace WebApp.Endpoints
Organisation? org = await guhf.GetOrganisationFromToken(token);
// Jeśli token należy do organizacji, która utworzyła to wydarzenie,
// to zwróć także EventRegistrations. W przeciwnym razie usuń to pole
// przed jego wysłaniem!
if (org is null || org.OrganisationId != Eve.OrganisationId) Eve.EventRegistrations = [];
// to zwróć także EventRegistrations. W przeciwnym razie niech będzie to
// puste pole.
List<EventDetailsDto> result = await guhf.BuildDetailedEventsDto(
dbContext,
(org is not null && Eve.Organisation == org)
);
// DLACZEGO?
Eve.Organisation = await guhf.GetOrganisationFromId(Eve.OrganisationId);
EventDetailsDto EveDto = Eve.ToEventDetailsDto();
return Results.Ok(EveDto); //EventDetailsDto
return Results.Ok(result.FirstOrDefault(e => e.EventId == id));
})
.WithName(GetEventEndpointName);
@@ -214,6 +217,9 @@ namespace WebApp.Endpoints
// UWAGA! TO NIE POWINNO TAK DZIAŁAĆ!
// KTOKOLWIEK WIDZIAŁ, KTOKOLWIEK WIE CZEMU Organisation JEST null?
//
// Odpowiedź: Bo pobieramy dane bez .Include(e => e.Organisation),
// co zapobiega masie innych problemów, m.in. rekurencyjnym importom.
e.Organisation = await guhf.GetOrganisationFromId(e.OrganisationId);
if (matchFound) SearchResults.Add(e.ToEventSummaryDto());

View File

@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp.Data;
using WebApp.DTOs;
using WebApp.Entities;
namespace WebApp.Endpoints;
@@ -112,4 +113,49 @@ public class GeneralUseHelpers
// Sprawdza, czy któreś ze słów pasuje (nawet częściowo) do searchTerm
return words.Any(word => word.Contains(searchTerm, StringComparison.OrdinalIgnoreCase));
}
public async Task<List<EventDetailsDto>> BuildDetailedEventsDto(ApplicationDbContext context, bool includeEventRegistrations = false)
{
// https://khalidabuhakmeh.com/ef-core-and-aspnet-core-cycle-issue-and-solution
// Jeśli token należy do organizacji, która utworzyła to wydarzenie,
// to zwróć także EventRegistrations. W przeciwnym razie niech będzie to
// puste pole.
ICollection<EventRegistrationDto> ERs = new List<EventRegistrationDto>();
if (includeEventRegistrations)
{
ERs = await context
.EventRegistrations
.Select(er => new EventRegistrationDto
{
EventId = er.EventId,
UserId = er.UserId,
UserName = er.User.FirstName + " " + er.User.LastName,
RegisteredAt = er.RegisteredAt
}).ToListAsync();
}
List<EventDetailsDto> result = await context
.Events
.Select(e => new EventDetailsDto
{
EventId = e.EventId,
OrganisationId = e.OrganisationId,
OrganisationName = e.Organisation.Name,
Title = e.Title,
Description = e.Description,
Location = e.Location,
EventDate = e.EventDate,
EventSkills = e
.EventSkills
.Select(es => new SkillSummaryDto
{
SkillId = es.SkillId,
SkillName = es.Skill.Name
}).ToList(),
EventRegistrations = ERs
}).ToListAsync();
return result;
}
}

View File

@@ -1,4 +1,4 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp.DTOs;
using WebApp.Entities;
@@ -34,20 +34,21 @@ public static class EventMapping
public static EventSummaryDto ToEventSummaryDto(this Event myEvent)
{
return new EventSummaryDto(
myEvent.EventId,
myEvent.Organisation!.Name,
myEvent.OrganisationId,
myEvent.Title,
myEvent.Description,
myEvent.Location,
myEvent.EventDate,
myEvent.EventSkills,
myEvent.EventRegistrations
);
return new EventSummaryDto {
EventId = myEvent.EventId,
Organisation = myEvent.Organisation!.Name,
OrganisationId = myEvent.OrganisationId,
Title = myEvent.Title,
Description = myEvent.Description,
Location = myEvent.Location,
EventDate = myEvent.EventDate,
EventSkills = myEvent.EventSkills,
EventRegistrations = myEvent.EventRegistrations
};
}
public static EventSummaryNoErDto ToEventSummaryNoErDto(this Event myEvent)
{
return new EventSummaryNoErDto(
myEvent.EventId,
myEvent.Organisation!.Name,
@@ -60,18 +61,42 @@ public static class EventMapping
);
}
public static EventRegistrationDto ToEventRegistrationDto(this EventRegistration myER)
{
return new EventRegistrationDto {
EventId = myER.EventId,
UserId = myER.UserId,
UserName = myER.User.FirstName + " " + myER.User.LastName,
RegisteredAt = myER.RegisteredAt
};
}
public static EventDetailsDto ToEventDetailsDto(this Event myEvent)
{
return new EventDetailsDto(
myEvent.EventId,
myEvent.OrganisationId,
myEvent.Organisation.Name,
myEvent.Title,
myEvent.Description,
myEvent.Location,
myEvent.EventDate,
myEvent.EventSkills,
myEvent.EventRegistrations
);
List<SkillSummaryDto> ssdto = new List<SkillSummaryDto>();
List<EventRegistrationDto> erdto = new List<EventRegistrationDto>();
foreach (EventSkill es in myEvent.EventSkills)
{
ssdto.Add(es.ToSkillSummaryDto());
}
foreach (EventRegistration er in myEvent.EventRegistrations)
{
erdto.Add(er.ToEventRegistrationDto());
}
return new EventDetailsDto {
EventId = myEvent.EventId,
OrganisationId = myEvent.OrganisationId,
OrganisationName = myEvent.Organisation.Name,
Title = myEvent.Title,
Description = myEvent.Description,
Location = myEvent.Location,
EventDate = myEvent.EventDate,
EventSkills = ssdto,
EventRegistrations = erdto
};
}
}

View File

@@ -13,4 +13,12 @@ public static class EventSkillMapping
SkillId = SSDto.Skill,
};
}
public static SkillSummaryDto ToSkillSummaryDto(this EventSkill es)
{
return new SkillSummaryDto{
SkillId = es.SkillId,
SkillName = es.Skill.Name
};
}
}

View File

@@ -1,4 +1,4 @@
using WebApp.DTOs;
using WebApp.DTOs;
using WebApp.Entities;
namespace WebApp.Mapping
@@ -16,10 +16,10 @@ namespace WebApp.Mapping
public static SkillSummaryDto ToSkillSummaryDto(this Skill s)
{
return new SkillSummaryDto(
s.SkillId,
s.Name
);
return new SkillSummaryDto {
SkillId = s.SkillId,
SkillName = s.Name
};
}
}
}