Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public async Task GetOrganizerEvents_WhenPaginationSucceeds_ShouldReturnPaginate
);

var organizerEvents = organizer.Events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEventsByOranizer(organizer)).Returns(organizerEvents);
eventRepositoryMock.Setup(p => p.GetEventsByOranizerAsync(organizer)).ReturnsAsync(organizerEvents);

paginationServiceMock
.Setup(p => p.PaginateAsync(organizerEvents, pageSize, page))
Expand Down Expand Up @@ -425,7 +425,7 @@ public async Task GetOrganizerEvents_WhenPaginationFails_ShouldPropagateError()
var ticketServiceMock = new Mock<ITicketService>();

var organizerEvents = organizer.Events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEventsByOranizer(organizer)).Returns(organizerEvents);
eventRepositoryMock.Setup(p => p.GetEventsByOranizerAsync(organizer)).ReturnsAsync(organizerEvents);

paginationServiceMock
.Setup(p => p.PaginateAsync(organizerEvents, pageSize, page))
Expand Down Expand Up @@ -477,7 +477,7 @@ public async Task GetEventsAsync_WhenPaginationSucceeds_ShouldReturnPaginatedEve
);

var eventsQueryable = events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable);
eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable);

paginationServiceMock
.Setup(p => p.PaginateAsync(eventsQueryable, pageSize, page))
Expand Down Expand Up @@ -541,7 +541,7 @@ public async Task GetEventsAsync_WhenPaginationFails_ShouldPropagateError()
var ticketServiceMock = new Mock<ITicketService>();

var eventsQueryable = events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable);
eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable);

paginationServiceMock
.Setup(p => p.PaginateAsync(eventsQueryable, pageSize, page))
Expand Down Expand Up @@ -583,7 +583,7 @@ public async Task GetEventsPaginationDetailsAsync_WhenSuccessful_ShouldReturnPag
var ticketServiceMock = new Mock<ITicketService>();

var eventsQueryable = events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable);
eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable);

var paginationDetails = new PaginationDetails(1, 3);
paginationServiceMock
Expand Down Expand Up @@ -625,7 +625,7 @@ public async Task GetEventsPaginationDetailsAsync_WhenFails_ShouldReturnError()
var ticketServiceMock = new Mock<ITicketService>();

var eventsQueryable = events.AsQueryable();
eventRepositoryMock.Setup(p => p.GetEvents()).Returns(eventsQueryable);
eventRepositoryMock.Setup(p => p.GetEventsAsync()).ReturnsAsync(eventsQueryable);

paginationServiceMock
.Setup(p => p.GetPaginationDetailsAsync(eventsQueryable, pageSize))
Expand Down
4 changes: 2 additions & 2 deletions TickAPI/TickAPI.Tests/Tickets/Services/TicketServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -537,8 +537,8 @@ public async Task GetTicketsForCustomerAsync_WithValidInput_ReturnsSuccessResult
false,
new PaginationDetails(0, 2)
);
var mappedData1 = new GetTicketForCustomerDto(tickets[0].Id, "EventName", new DateTime(2025, 10, 10), new DateTime(2025, 10, 20), false);
var mappedData2 = new GetTicketForCustomerDto(tickets[1].Id, "EventName2", new DateTime(2025, 11, 10), new DateTime(2025, 11, 20), false);
var mappedData1 = new GetTicketForCustomerDto(tickets[0].Id, "EventName", new DateTime(2025, 10, 10), new DateTime(2025, 10, 20), false, false, null, null);
var mappedData2 = new GetTicketForCustomerDto(tickets[1].Id, "EventName2", new DateTime(2025, 11, 10), new DateTime(2025, 11, 20), false, false, null, null);
var mappedPaginatedData = new PaginatedData<GetTicketForCustomerDto>
(
new List<GetTicketForCustomerDto>{mappedData1, mappedData2},
Expand Down
5 changes: 3 additions & 2 deletions TickAPI/TickAPI/Common/Mail/Abstractions/IMailService.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using TickAPI.Common.Mail.Models;
using TickAPI.Common.Results;
using TickAPI.Customers.Models;
using TickAPI.Tickets.Models;

namespace TickAPI.Common.Mail.Abstractions;

public interface IMailService
{
public Task<Result> SendTicketAsync(MailRecipient recipient, string eventName, byte[] pdfData);

public Task<Result> SendTicketsAsync(Customer customer, List<Ticket> tickets);
public Task<Result> SendMailAsync(IEnumerable<MailRecipient> recipients, string subject, string content, List<MailAttachment>? attachments);
}
37 changes: 25 additions & 12 deletions TickAPI/TickAPI/Common/Mail/Services/MailService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using SendGrid;
using System.Text;
using SendGrid;
using SendGrid.Helpers.Mail;
using TickAPI.Common.Mail.Abstractions;
using TickAPI.Common.Mail.Models;
using TickAPI.Common.Results;
using TickAPI.Customers.Models;
using TickAPI.Tickets.Models;

namespace TickAPI.Common.Mail.Services;

Expand All @@ -19,19 +22,29 @@ public MailService(IConfiguration configuration)
var fromName = configuration["SendGrid:FromName"];
_fromEmailAddress = new EmailAddress(fromEmail, fromName);
}

public async Task<Result> SendTicketAsync(MailRecipient recipient, string eventName, byte[] pdfData)
public async Task<Result> SendTicketsAsync(Customer customer, List<Ticket> tickets)
{
var subject = $"Ticket for {eventName}";
var htmlContent = "<strong>Download your ticket from attachments</strong>";
var base64Content = Convert.ToBase64String(pdfData);
List<MailAttachment> attachments = [
new MailAttachment("ticket.pdf", base64Content, "application/pdf")
];
var res = await SendMailAsync([recipient], subject, htmlContent, attachments);
return res;
}
var subject = "Your New Tickets";
var htmlContent = new StringBuilder();
htmlContent.AppendLine("<strong>You have purchased tickets for following events:</strong><br/><ul>");

foreach (var ticket in tickets)
{
var eventName = ticket.Type.Event.Name;
var eventDate = ticket.Type.Event.StartDate.ToString("yyyy-MM-dd");

htmlContent.AppendLine(
$"<li><b>{eventName}</b> on {eventDate} (ticket: {ticket.Type.Description})</li>"
);
}

htmlContent.AppendLine("</ul>");

var recipient = new MailRecipient(customer.Email, customer.FirstName);
return await SendMailAsync([recipient], subject, htmlContent.ToString(), []);
}

public async Task<Result> SendMailAsync(IEnumerable<MailRecipient> recipients, string subject, string content,
List<MailAttachment>? attachments = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public TickApiDbContext(DbContextOptions<TickApiDbContext> options) : base(optio
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Ticket>().Property(t => t.ResellPrice).HasColumnType("decimal(18,2)");
modelBuilder.Entity<Ticket>().Property(t => t.ResellPrice).IsRequired(false);
modelBuilder.Entity<TicketType>().Property(t => t.Price).HasColumnType("decimal(18,2)");
modelBuilder.Entity<Category>().HasData(
new Category
Expand Down
4 changes: 2 additions & 2 deletions TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace TickAPI.Events.Abstractions;
public interface IEventRepository
{
public Task AddNewEventAsync(Event @event);
public IQueryable<Event> GetEvents();
public IQueryable<Event> GetEventsByOranizer(Organizer organizer);
public Task<IQueryable<Event>> GetEventsAsync();
public Task<IQueryable<Event>> GetEventsByOranizerAsync(Organizer organizer);
public Task<Result<Event>> GetEventByIdAsync(Guid eventId);
public Task<Result> SaveEventAsync(Event ev);
public Task<Result<Event>> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer);
Expand Down
100 changes: 100 additions & 0 deletions TickAPI/TickAPI/Events/Repositories/BaseEventRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using Microsoft.EntityFrameworkCore;
using TickAPI.Common.Results;
using TickAPI.Common.Results.Generic;
using TickAPI.Common.TickApiDbContext;
using TickAPI.Events.Abstractions;
using TickAPI.Events.Models;
using TickAPI.Organizers.Models;

namespace TickAPI.Events.Repositories;

public class BaseEventRepository : IEventRepository
{
private readonly TickApiDbContext _tickApiDbContext;

public BaseEventRepository(TickApiDbContext tickApiDbContext)
{
_tickApiDbContext = tickApiDbContext;
}

public async Task AddNewEventAsync(Event @event)
{
_tickApiDbContext.Events.Add(@event);
await _tickApiDbContext.SaveChangesAsync();
}

public async Task<IQueryable<Event>> GetEventsAsync()
{
return _tickApiDbContext.Events
.Include(e => e.Address)
.Include(e => e.TicketTypes)
.Include(e => e.Categories);
}

public async Task<IQueryable<Event>> GetEventsByOranizerAsync(Organizer organizer)
{
return _tickApiDbContext.Events
.Include(e => e.Address)
.Include(e => e.TicketTypes)
.Include(e => e.Categories)
.Where(e => e.Organizer.Id == organizer.Id);
}

public async Task<Result<Event>> GetEventByIdAsync(Guid eventId)
{
var @event = await _tickApiDbContext.Events
.Include(e => e.Address)
.Include(e => e.TicketTypes)
.Include(e => e.Categories)
.FirstOrDefaultAsync(e => e.Id == eventId);

if (@event == null)
{
return Result<Event>.Failure(StatusCodes.Status404NotFound, $"event with id {eventId} not found");
}

return Result<Event>.Success(@event);
}

public async Task<Result> SaveEventAsync(Event ev)
{
var fromDb = await GetEventByIdAsync(ev.Id);
if (fromDb.IsError)
return Result.PropagateError(fromDb);
await _tickApiDbContext.SaveChangesAsync();
return Result.Success();
}

public async Task<Result<Event>> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer)
{
var organizerEvents = GetEventsByOranizerAsync(organizer);
var ev = await (await organizerEvents).Where(e => e.Id == eventId).FirstAsync();
if (ev is null)
{
return Result<Event>.Failure(StatusCodes.Status404NotFound, $"Event with id {eventId} not found for organizer with id {organizer.Id}");
}
return Result<Event>.Success(ev);
}

public async Task<decimal> GetEventRevenue(Guid eventId)
{
var query = from tickets in _tickApiDbContext.Tickets
join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id
join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id
where events.Id == eventId
select new { price = _ticketTypes.Price };
var val = await query.SumAsync(x => x.price);
return val;
}

public async Task<int> GetEventSoldTicketsCount(Guid eventId)
{
var query = from tickets in _tickApiDbContext.Tickets
join _ticketTypes in _tickApiDbContext.TicketTypes on tickets.Type.Id equals _ticketTypes.Id
join events in _tickApiDbContext.Events on _ticketTypes.Event.Id equals events.Id
where events.Id == eventId
select new { id = tickets.Id };
var val = await query.CountAsync();
return val;
}
}
Loading
Loading