diff --git a/backend/src/MethodConf.Cms/Controllers/ConferenceIssueController.cs b/backend/src/MethodConf.Cms/Controllers/ConferenceIssueController.cs index ce07e82..1d64586 100644 --- a/backend/src/MethodConf.Cms/Controllers/ConferenceIssueController.cs +++ b/backend/src/MethodConf.Cms/Controllers/ConferenceIssueController.cs @@ -1,8 +1,8 @@ using Asp.Versioning; -using AutoMapper; using MethodConf.Cms.Domain; using MethodConf.Cms.Domain.Errors; using MethodConf.Cms.Dtos; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Microsoft.AspNetCore.Mvc; @@ -11,19 +11,19 @@ namespace MethodConf.Cms.Controllers; [ApiController] [ApiVersion("1.0")] [Route(RouteTemplates.ConferenceIssue)] -public class ConferenceIssueController(IConferenceIssueService conferenceIssueService, IMapper mapper) : Controller +public class ConferenceIssueController(IConferenceIssueService conferenceIssueService, IssueMapper mapper) : Controller { [HttpPost] public async Task> CreateIssue(Guid conferenceId, CreateIssueRequestDto request) { - var createIssue = mapper.Map(request); + var createIssue = mapper.ToCreateIssue(request); var result = await conferenceIssueService.CreateIssue(conferenceId, createIssue); return result switch { { IsFailed: true } when result.Errors.Any(e => e is InvalidEntityIdError) => NotFound(result.Errors), - { IsSuccess: true } => Ok(mapper.Map(result.Value)), + { IsSuccess: true } => Ok(mapper.ToCreateIssueResponseDto(result.Value)), _ => StatusCode(500) }; } diff --git a/backend/src/MethodConf.Cms/Controllers/ConferenceScheduleController.cs b/backend/src/MethodConf.Cms/Controllers/ConferenceScheduleController.cs index 68af0de..d876c52 100644 --- a/backend/src/MethodConf.Cms/Controllers/ConferenceScheduleController.cs +++ b/backend/src/MethodConf.Cms/Controllers/ConferenceScheduleController.cs @@ -1,7 +1,7 @@ using Asp.Versioning; -using AutoMapper; using MethodConf.Cms.Domain.Errors; using MethodConf.Cms.Dtos; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Microsoft.AspNetCore.Mvc; @@ -10,7 +10,7 @@ namespace MethodConf.Cms.Controllers; [ApiController] [ApiVersion("1.0")] [Route(RouteTemplates.ConferenceSchedule)] -public class ConferenceScheduleController(IConferenceScheduleService conferenceScheduleService, IMapper mapper) : Controller +public class ConferenceScheduleController(IConferenceScheduleService conferenceScheduleService, ScheduleMapper mapper) : Controller { [HttpGet] public ActionResult GetSchedule(Guid conferenceId) @@ -20,7 +20,7 @@ public ActionResult GetSchedule(Guid conferenceId return result switch { { IsFailed: true } when result.Errors.Any(e => e is InvalidEntityIdError) => NotFound(result.Errors), - { IsSuccess: true } => Ok(mapper.Map(result.Value)), + { IsSuccess: true } => Ok(mapper.ToDto(result.Value)), _ => StatusCode(500) }; } diff --git a/backend/src/MethodConf.Cms/Controllers/SessionFeedbackController.cs b/backend/src/MethodConf.Cms/Controllers/SessionFeedbackController.cs index 342d787..ab54855 100644 --- a/backend/src/MethodConf.Cms/Controllers/SessionFeedbackController.cs +++ b/backend/src/MethodConf.Cms/Controllers/SessionFeedbackController.cs @@ -1,7 +1,7 @@ -using AutoMapper; using MethodConf.Cms.Domain; using MethodConf.Cms.Domain.Errors; using MethodConf.Cms.Dtos; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Microsoft.AspNetCore.Mvc; @@ -9,19 +9,19 @@ namespace MethodConf.Cms.Controllers; [ApiController] [Route(RouteTemplates.SessionFeedback)] -public class SessionFeedbackController(ISessionFeedbackService sessionFeedbackService, IMapper mapper) : Controller +public class SessionFeedbackController(ISessionFeedbackService sessionFeedbackService, SessionFeedbackMapper mapper) : Controller { [HttpPost] public async Task> Create(Guid sessionId, CreateSessionFeedbackRequestDto request) { - var createSessionFeedback = mapper.Map(request); + var createSessionFeedback = mapper.ToCreateSessionFeedback(request); var result = await sessionFeedbackService.Create(sessionId, createSessionFeedback); return result switch { { IsFailed: true } when result.Errors.Any(e => e is InvalidEntityIdError) => BadRequest(result.Errors), - { IsSuccess: true } => Ok(mapper.Map(result.Value)), + { IsSuccess: true } => Ok(mapper.ToDto(result.Value)), _ => StatusCode(500) }; } diff --git a/backend/src/MethodConf.Cms/Mapping/DefaultProfile.cs b/backend/src/MethodConf.Cms/Mapping/DefaultProfile.cs deleted file mode 100644 index b05dc30..0000000 --- a/backend/src/MethodConf.Cms/Mapping/DefaultProfile.cs +++ /dev/null @@ -1,33 +0,0 @@ -using AutoMapper; -using MethodConf.Cms.Domain; -using MethodConf.Cms.Dtos; -using Umbraco.Cms.Web.Common.PublishedModels; - -namespace MethodConf.Cms.Mapping; - -public class DefaultProfile : Profile -{ - public DefaultProfile() - { - CreateMap(); - CreateMap() - .ForMember(dest => dest.Key, opts => opts.MapFrom()); - CreateMap() - .ForMember(dest => dest.Sessions, opts => opts.MapFrom((src, _) => src.Children() ?? [])); - - CreateMap(); - CreateMap(); - CreateMap(); - - CreateMap() - .ForMember(dest => dest.PhoneNumber, opts => opts.MapFrom(src => src.Phone)); - CreateMap(); - CreateMap(); - CreateMap() - .ForMember(dest => dest.Phone, opts => opts.MapFrom(src => src.PhoneNumber)); - CreateMap() - .ForMember(dest => dest.Phone, opts => opts.MapFrom(src => src.PhoneNumber)); - CreateMap() - .ForMember(dest => dest.Phone, opts => opts.MapFrom(src => src.PhoneNumber)); - } -} diff --git a/backend/src/MethodConf.Cms/Mapping/IssueMapper.cs b/backend/src/MethodConf.Cms/Mapping/IssueMapper.cs new file mode 100644 index 0000000..50477e5 --- /dev/null +++ b/backend/src/MethodConf.Cms/Mapping/IssueMapper.cs @@ -0,0 +1,38 @@ +using MethodConf.Cms.Domain; +using MethodConf.Cms.Dtos; +using Riok.Mapperly.Abstractions; + +namespace MethodConf.Cms.Mapping; + +[Mapper] +public partial class IssueMapper +{ + [MapProperty(nameof(CreateIssueRequestDto.Phone), nameof(CreateIssue.PhoneNumber))] + public partial CreateIssue ToCreateIssue(CreateIssueRequestDto src); + + [MapperIgnoreTarget(nameof(Issue.Id))] + [MapperIgnoreTarget(nameof(Issue.CreatedAt))] + public partial Issue ToIssue(CreateIssue src, Guid conferenceId); + + [MapperIgnoreSource(nameof(IssueWithResponse.Id))] + [MapperIgnoreSource(nameof(IssueWithResponse.ConferenceId))] + [MapProperty(nameof(IssueWithResponse.PhoneNumber), nameof(CreateIssueResponseDto.Phone))] + public partial CreateIssueResponseDto ToCreateIssueResponseDto(IssueWithResponse src); + + [MapperIgnoreSource(nameof(Issue.Id))] + [MapperIgnoreSource(nameof(Issue.ConferenceId))] + [MapperIgnoreSource(nameof(Issue.CreatedAt))] + [MapperIgnoreTarget(nameof(NewIssueEmailViewModel.Title))] + [MapProperty(nameof(Issue.PhoneNumber), nameof(NewIssueEmailViewModel.Phone))] + public partial NewIssueEmailViewModel ToNewIssueEmailViewModel(Issue src); + + [MapperIgnoreSource(nameof(Issue.Id))] + [MapperIgnoreSource(nameof(Issue.ConferenceId))] + [MapperIgnoreSource(nameof(Issue.CreatedAt))] + [MapProperty(nameof(Issue.PhoneNumber), nameof(NewIssueAppResponseViewModel.Phone))] + public partial NewIssueAppResponseViewModel ToNewIssueAppResponseViewModel(Issue src); + + [MapperIgnoreSource(nameof(Issue.CreatedAt))] + [MapValue(nameof(IssueWithResponse.ResponseMarkup), "")] + public partial IssueWithResponse ToIssueWithResponse(Issue src); +} diff --git a/backend/src/MethodConf.Cms/Mapping/ScheduleMapper.cs b/backend/src/MethodConf.Cms/Mapping/ScheduleMapper.cs new file mode 100644 index 0000000..5749f5c --- /dev/null +++ b/backend/src/MethodConf.Cms/Mapping/ScheduleMapper.cs @@ -0,0 +1,29 @@ +using MethodConf.Cms.Domain; +using MethodConf.Cms.Dtos; +using Riok.Mapperly.Abstractions; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.Common.PublishedModels; + +namespace MethodConf.Cms.Mapping; + +[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] +public partial class ScheduleMapper(IDocumentUrlService documentUrlService) +{ + [MapperRequiredMapping(RequiredMappingStrategy.Both)] + public partial ConferenceScheduleResponseDto ToDto(ConferenceSchedule src); + + [MapProperty(nameof(Session.Key), nameof(SessionItem.Key), Use = nameof(ResolveSessionKey))] + public partial SessionItem ToSessionItem(Session src); + + [MapPropertyFromSource(nameof(TrackItem.Sessions), Use = nameof(MapTrackSessions))] + public partial TrackItem ToTrackItem(Track src); + + private SessionItem[] MapTrackSessions(Track src) + { + var sessions = src.Children() ?? []; + return sessions.Select(ToSessionItem).ToArray(); + } + + private string ResolveSessionKey(Guid key) => + documentUrlService.GetUrlSegment(key, "", false) ?? key.ToString(); +} diff --git a/backend/src/MethodConf.Cms/Mapping/SessionFeedbackMapper.cs b/backend/src/MethodConf.Cms/Mapping/SessionFeedbackMapper.cs new file mode 100644 index 0000000..765d27a --- /dev/null +++ b/backend/src/MethodConf.Cms/Mapping/SessionFeedbackMapper.cs @@ -0,0 +1,21 @@ +using MethodConf.Cms.Domain; +using MethodConf.Cms.Dtos; +using Riok.Mapperly.Abstractions; + +namespace MethodConf.Cms.Mapping; + +[Mapper] +public partial class SessionFeedbackMapper +{ + public partial CreateSessionFeedback ToCreateSessionFeedback(CreateSessionFeedbackRequestDto src); + + [MapperIgnoreSource(nameof(SessionFeedback.SessionId))] + [MapperIgnoreSource(nameof(SessionFeedback.CreatedAt))] + public partial SessionFeedbackResponseDto ToDto(SessionFeedback src); + + [MapValue(nameof(SessionFeedback.Id), Use = nameof(EmptyGuid))] + [MapperIgnoreTarget(nameof(SessionFeedback.CreatedAt))] + public partial SessionFeedback ToSessionFeedback(CreateSessionFeedback src, Guid sessionId); + + private static Guid EmptyGuid() => Guid.Empty; +} diff --git a/backend/src/MethodConf.Cms/Mapping/SessionUrlSegmentResolver.cs b/backend/src/MethodConf.Cms/Mapping/SessionUrlSegmentResolver.cs deleted file mode 100644 index e4566c1..0000000 --- a/backend/src/MethodConf.Cms/Mapping/SessionUrlSegmentResolver.cs +++ /dev/null @@ -1,21 +0,0 @@ -using AutoMapper; -using MethodConf.Cms.Domain; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Web.Common.PublishedModels; - -namespace MethodConf.Cms.Mapping; - -public class SessionUrlSegmentResolver : IValueResolver -{ - private readonly IDocumentUrlService _documentUrlService; - - public SessionUrlSegmentResolver(IDocumentUrlService documentUrlService) - { - _documentUrlService = documentUrlService; - } - - public string Resolve(Session source, SessionItem destination, string destMember, ResolutionContext context) - { - return _documentUrlService.GetUrlSegment(source.Key, "", false) ?? source.Key.ToString(); - } -} diff --git a/backend/src/MethodConf.Cms/MethodConf.Cms.csproj b/backend/src/MethodConf.Cms/MethodConf.Cms.csproj index f632859..7622a31 100644 --- a/backend/src/MethodConf.Cms/MethodConf.Cms.csproj +++ b/backend/src/MethodConf.Cms/MethodConf.Cms.csproj @@ -10,7 +10,6 @@ - @@ -18,6 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/src/MethodConf.Cms/Services/ConferenceIssueService.cs b/backend/src/MethodConf.Cms/Services/ConferenceIssueService.cs index f1c546f..7c3e5fa 100644 --- a/backend/src/MethodConf.Cms/Services/ConferenceIssueService.cs +++ b/backend/src/MethodConf.Cms/Services/ConferenceIssueService.cs @@ -1,9 +1,9 @@ -using AutoMapper; using FluentResults; using MethodConf.Cms.Domain; using MethodConf.Cms.Domain.Errors; using MethodConf.Cms.Dtos; using MethodConf.Cms.Infrastructure; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Microsoft.Extensions.Options; using Razor.Templating.Core; @@ -21,7 +21,7 @@ public class ConferenceIssueService( IEmailSender emailSender, IRazorTemplateEngine razorEngine, IOptions globalSettings, - IMapper mapper) : IConferenceIssueService + IssueMapper mapper) : IConferenceIssueService { private const string OrganizerEmailSubject = "New Issue Reported for Method Conf"; private const string ReporterEmailSubject = "Your Issue Has Been Recieved"; @@ -33,8 +33,7 @@ public async Task> CreateIssue(Guid conferenceId, Crea return Result.Fail(new InvalidEntityIdError(conferenceId.ToString())); } - var newIssue = mapper.Map(createIssue); - newIssue.ConferenceId = conference.Key; + var newIssue = mapper.ToIssue(createIssue, conference.Key); newIssue.CreatedAt = DateTime.UtcNow; dbContext.Add(newIssue); @@ -56,7 +55,7 @@ public async Task> CreateIssue(Guid conferenceId, Crea await SendOrganizerEmail(newIssue); } - var issueWithResponse = mapper.Map(newIssue); + var issueWithResponse = mapper.ToIssueWithResponse(newIssue); issueWithResponse.ResponseMarkup = await GetResponseMessage(newIssue) ?? ""; return issueWithResponse; @@ -64,7 +63,7 @@ public async Task> CreateIssue(Guid conferenceId, Crea private async Task SendOrganizerEmail(Issue issue) { - var vm = mapper.Map(issue); + var vm = mapper.ToNewIssueEmailViewModel(issue); vm.Title = OrganizerEmailSubject; var content = await razorEngine.RenderAsync("~/Views/Templates/NewIssueOrganizerEmail.cshtml", vm); @@ -82,7 +81,7 @@ private async Task SendOrganizerEmail(Issue issue) private async Task SendReporterEmail(string reporterEmail, Issue issue) { - var vm = mapper.Map(issue); + var vm = mapper.ToNewIssueEmailViewModel(issue); vm.Title = ReporterEmailSubject; var content = await razorEngine.RenderAsync("~/Views/Templates/NewIssueReporterEmail.cshtml", vm); @@ -102,7 +101,7 @@ private async Task SendReporterEmail(string reporterEmail, Issue issue) public async Task GetResponseMessage(Issue issue) { - var vm = mapper.Map(issue); + var vm = mapper.ToNewIssueAppResponseViewModel(issue); return await razorEngine.RenderAsync("~/Views/Templates/NewIssueAppResponse.cshtml", vm); } } diff --git a/backend/src/MethodConf.Cms/Services/ConferenceScheduleService.cs b/backend/src/MethodConf.Cms/Services/ConferenceScheduleService.cs index 4d84de6..3420719 100644 --- a/backend/src/MethodConf.Cms/Services/ConferenceScheduleService.cs +++ b/backend/src/MethodConf.Cms/Services/ConferenceScheduleService.cs @@ -1,7 +1,7 @@ -using AutoMapper; using FluentResults; using MethodConf.Cms.Domain; using MethodConf.Cms.Domain.Errors; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Web; @@ -9,7 +9,7 @@ namespace MethodConf.Cms.Services; -public class ConferenceScheduleService(IPublishedContentQuery publishedContentQuery, IScheduleGridGenerator scheduleGridGenerator, IMapper mapper) : IConferenceScheduleService +public class ConferenceScheduleService(IPublishedContentQuery publishedContentQuery, IScheduleGridGenerator scheduleGridGenerator, ScheduleMapper mapper) : IConferenceScheduleService { public Result GetSchedule(Guid conferenceId) { @@ -27,10 +27,10 @@ public Result GetSchedule(Guid conferenceId) switch (sessionOrTrack) { case Session session: - scheduleItems.Add(mapper.Map(session)); + scheduleItems.Add(mapper.ToSessionItem(session)); continue; case Track track: - scheduleItems.Add(mapper.Map(track)); + scheduleItems.Add(mapper.ToTrackItem(track)); continue; } } diff --git a/backend/src/MethodConf.Cms/Services/DependencyInjection.cs b/backend/src/MethodConf.Cms/Services/DependencyInjection.cs index 3a048d7..b0c6c86 100644 --- a/backend/src/MethodConf.Cms/Services/DependencyInjection.cs +++ b/backend/src/MethodConf.Cms/Services/DependencyInjection.cs @@ -11,7 +11,9 @@ public static IHostApplicationBuilder AddApplicationServices(this IHostApplicati builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - builder.Services.AddAutoMapper(_ => { }, typeof(DefaultProfile).Assembly); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddRazorTemplating(); return builder; diff --git a/backend/src/MethodConf.Cms/Services/SessionFeedbackService.cs b/backend/src/MethodConf.Cms/Services/SessionFeedbackService.cs index ee0d162..5d71127 100644 --- a/backend/src/MethodConf.Cms/Services/SessionFeedbackService.cs +++ b/backend/src/MethodConf.Cms/Services/SessionFeedbackService.cs @@ -1,15 +1,15 @@ -using AutoMapper; using FluentResults; using MethodConf.Cms.Domain; using MethodConf.Cms.Domain.Errors; using MethodConf.Cms.Infrastructure; +using MethodConf.Cms.Mapping; using MethodConf.Cms.Services.Interfaces; using Umbraco.Cms.Core; using Umbraco.Cms.Web.Common.PublishedModels; namespace MethodConf.Cms.Services; -public class SessionFeedbackService(IPublishedContentQuery publishedContentQuery, AppDbContext dbContext, IMapper mapper) : ISessionFeedbackService +public class SessionFeedbackService(IPublishedContentQuery publishedContentQuery, AppDbContext dbContext, SessionFeedbackMapper mapper) : ISessionFeedbackService { public async Task> Create(Guid sessionId, CreateSessionFeedback createSessionFeedback) @@ -24,8 +24,7 @@ public async Task> Create(Guid sessionId, CreateSessionF return new IneligibleForFeedback(session); } - var sessionFeedback = mapper.Map(createSessionFeedback); - sessionFeedback.SessionId = session.Key; + var sessionFeedback = mapper.ToSessionFeedback(createSessionFeedback, session.Key); sessionFeedback.CreatedAt = DateTime.UtcNow;