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
842 changes: 842 additions & 0 deletions TickAPI/TickAPI.Tests/Events/Services/EventServiceTests.cs

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions TickAPI/TickAPI/Addresses/DTOs/Request/CreateAddressDto.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using TickAPI.Events.Models;

namespace TickAPI.Addresses.DTOs.Request;
namespace TickAPI.Addresses.DTOs.Request;

public record CreateAddressDto(

string Country,
string City,
string? Street,
uint? HouseNumber,
uint? FlatNumber,
string PostalCode);
string PostalCode
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace TickAPI.Categories.DTOs.Request;

public record EditEventCategoryDto(
string CategoryName
);
5 changes: 4 additions & 1 deletion TickAPI/TickAPI/Events/Abstractions/IEventRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TickAPI.Common.Results.Generic;
using TickAPI.Common.Results;
using TickAPI.Common.Results.Generic;
using TickAPI.Events.Models;
using TickAPI.Organizers.Models;

Expand All @@ -10,4 +11,6 @@ public interface IEventRepository
public IQueryable<Event> GetEvents();
public IQueryable<Event> GetEventsByOranizer(Organizer organizer);
public Task<Result<Event>> GetEventByIdAsync(Guid eventId);
public Task<Result> SaveEventAsync(Event ev);
public Task<Result<Event>> GetEventByIdAndOrganizerAsync(Guid eventId, Organizer organizer);
}
1 change: 1 addition & 0 deletions TickAPI/TickAPI/Events/Abstractions/IEventService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public Task<Result<Event>> CreateNewEventAsync(string name, string description,
public Task<Result<PaginatedData<GetEventResponseDto>>> GetEventsAsync(int page, int pageSize, EventFiltersDto? eventFilters = null);
public Task<Result<PaginationDetails>> GetEventsPaginationDetailsAsync(int pageSize);
public Task<Result<GetEventDetailsResponseDto>> GetEventDetailsAsync(Guid eventId);
public Task<Result<Event>> EditEventAsync(Organizer organizer, Guid eventId, string name, string description, DateTime startDate, DateTime endDate, uint? minimumAge, CreateAddressDto editAddress, List<EditEventCategoryDto> categories, EventStatus eventStatus);
}
27 changes: 27 additions & 0 deletions TickAPI/TickAPI/Events/Controllers/EventsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,31 @@ public async Task<ActionResult<GetEventDetailsResponseDto>> GetEventDetails([Fro
var eventDetailsResult = await _eventService.GetEventDetailsAsync(id);
return eventDetailsResult.ToObjectResult();
}

[AuthorizeWithPolicy(AuthPolicies.VerifiedOrganizerPolicy)]
[HttpPatch("{id:guid}")]
public async Task<ActionResult<EditEventResponseDto>> EditEvent([FromRoute] Guid id, [FromBody] EditEventDto request)
{
var emailResult = _claimsService.GetEmailFromClaims(User.Claims);
if (emailResult.IsError)
{
return emailResult.ToObjectResult();
}
var email = emailResult.Value!;

var organizerResult = await _organizerService.GetOrganizerByEmailAsync(email);
if (organizerResult.IsError)
{
return organizerResult.ToObjectResult();
}
var organizer = organizerResult.Value!;

var editedEventResult = await _eventService.EditEventAsync(organizer, id, request.Name, request.Description, request.StartDate, request.EndDate, request.MinimumAge,
request.EditAddress, request.Categories, request.EventStatus);

if (editedEventResult.IsError)
return editedEventResult.ToObjectResult();

return Ok("Event edited succesfully");
}
}
16 changes: 16 additions & 0 deletions TickAPI/TickAPI/Events/DTOs/Request/EditEventDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using TickAPI.Addresses.DTOs.Request;
using TickAPI.Categories.DTOs.Request;
using TickAPI.Events.Models;

namespace TickAPI.Events.DTOs.Request;

public record EditEventDto(
string Name,
string Description,
DateTime StartDate,
DateTime EndDate,
uint? MinimumAge,
List<EditEventCategoryDto> Categories,
EventStatus EventStatus,
CreateAddressDto EditAddress
);
3 changes: 3 additions & 0 deletions TickAPI/TickAPI/Events/DTOs/Response/EditEventResponseDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace TickAPI.Events.DTOs.Response;

public record EditEventResponseDto();
21 changes: 21 additions & 0 deletions TickAPI/TickAPI/Events/Repositories/EventRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using TickAPI.Common.Results;
using TickAPI.Common.Results.Generic;
using TickAPI.Common.TickApiDbContext;
using TickAPI.Events.Abstractions;
Expand Down Expand Up @@ -54,4 +55,24 @@ public async Task<Result<Event>> GetEventByIdAsync(Guid eventId)

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 = GetEventsByOranizer(organizer);
var ev = 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);
}
}
105 changes: 80 additions & 25 deletions TickAPI/TickAPI/Events/Services/EventService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using TickAPI.Common.Pagination.Responses;
using TickAPI.Categories.Abstractions;
using TickAPI.Categories.DTOs.Request;
using TickAPI.Common.Results;
using TickAPI.Common.Time.Abstractions;
using TickAPI.Events.Abstractions;
using TickAPI.Events.Models;
Expand Down Expand Up @@ -40,36 +41,13 @@ public EventService(IEventRepository eventRepository, IOrganizerService organize
_ticketService = ticketService;
}

public async Task<Result<Event>> CreateNewEventAsync(string name, string description, DateTime startDate, DateTime endDate,
public async Task<Result<Event>> CreateNewEventAsync(string name, string description, DateTime startDate, DateTime endDate,
uint? minimumAge, CreateAddressDto createAddress, List<CreateEventCategoryDto> categories, List<CreateEventTicketTypeDto> ticketTypes,
EventStatus eventStatus, string organizerEmail)
{
var organizerResult = await _organizerService.GetOrganizerByEmailAsync(organizerEmail);
if (!organizerResult.IsSuccess)
return Result<Event>.PropagateError(organizerResult);


if (endDate < startDate)
return Result<Event>.Failure(StatusCodes.Status400BadRequest, "End date should be after start date");

if (startDate < _dateTimeService.GetCurrentDateTime())
return Result<Event>.Failure(StatusCodes.Status400BadRequest, "Start date is in the past");

if (ticketTypes.Any(t => t.AvailableFrom > endDate))
{
return Result<Event>.Failure(StatusCodes.Status400BadRequest, "Tickets can't be available after the event is over");
}

var address = await _addressService.GetOrCreateAddressAsync(createAddress);

var categoryNames = categories.Select(c => c.CategoryName).ToList();

var categoriesByNameResult = _categoryService.GetCategoriesByNames(categoryNames);

if (categoriesByNameResult.IsError)
{
return Result<Event>.PropagateError(categoriesByNameResult);
}

var ticketTypesConverted = ticketTypes.Select(t => new TicketType
{
Expand All @@ -80,7 +58,23 @@ public async Task<Result<Event>> CreateNewEventAsync(string name, string descri
Price = t.Price,
})
.ToList();


var datesCheck = CheckEventDates(startDate, endDate, ticketTypesConverted);
if (datesCheck.IsError)
return Result<Event>.PropagateError(datesCheck);

var address = await _addressService.GetOrCreateAddressAsync(createAddress);
if (address.IsError)
{
return Result<Event>.PropagateError(address);
}
var categoryNames = categories.Select(c => c.CategoryName).ToList();
var categoriesByNameResult = _categoryService.GetCategoriesByNames(categoryNames);
if (categoriesByNameResult.IsError)
{
return Result<Event>.PropagateError(categoriesByNameResult);
}

var @event = new Event
{
Name = name,
Expand Down Expand Up @@ -163,6 +157,51 @@ public async Task<Result<GetEventDetailsResponseDto>> GetEventDetailsAsync(Guid
return Result<GetEventDetailsResponseDto>.Success(details);
}

public async Task<Result<Event>> EditEventAsync(Organizer organizer, Guid eventId, string name, string description, DateTime startDate, DateTime endDate, uint? minimumAge, CreateAddressDto editAddress, List<EditEventCategoryDto> categories,
EventStatus eventStatus)
{
var existingEventResult = await _eventRepository.GetEventByIdAndOrganizerAsync(eventId, organizer);
if (existingEventResult.IsError)
{
return existingEventResult;
}
var existingEvent = existingEventResult.Value!;

var datesCheck = CheckEventDates(startDate, endDate, existingEvent.TicketTypes, existingEvent.StartDate == startDate);
if (datesCheck.IsError)
return Result<Event>.PropagateError(datesCheck);

var address = await _addressService.GetOrCreateAddressAsync(editAddress);
if (address.IsError)
{
return Result<Event>.PropagateError(address);
}

var categoryNames = categories.Select(c => c.CategoryName).ToList();
var categoriesByNameResult = _categoryService.GetCategoriesByNames(categoryNames);
if (categoriesByNameResult.IsError)
{
return Result<Event>.PropagateError(categoriesByNameResult);
}

existingEvent.Name = name;
existingEvent.Description = description;
existingEvent.StartDate = startDate;
existingEvent.EndDate = endDate;
existingEvent.MinimumAge = minimumAge;
existingEvent.Address = address.Value!;
existingEvent.Categories = categoriesByNameResult.Value!;
existingEvent.EventStatus = eventStatus;

var saveResult = await _eventRepository.SaveEventAsync(existingEvent);
if (saveResult.IsError)
{
return Result<Event>.PropagateError(saveResult);
}

return Result<Event>.Success(existingEvent);
}

private async Task<Result<PaginatedData<GetEventResponseDto>>> GetPaginatedEventsAsync(IQueryable<Event> events, int page, int pageSize)
{
var paginatedEventsResult = await _paginationService.PaginateAsync(events, pageSize, page);
Expand Down Expand Up @@ -203,4 +242,20 @@ private static GetEventResponseDto MapEventToGetEventResponseDto(Event ev)
return new GetEventResponseDto(ev.Id, ev.Name, ev.Description, ev.StartDate, ev.EndDate, ev.MinimumAge,
minimumPrice, maximumPrice, categories, ev.EventStatus, address);
}

private Result CheckEventDates(DateTime startDate, DateTime endDate, IEnumerable<TicketType> ticketTypes, bool skipStartDateEvaluation = false)
{
if (endDate < startDate)
return Result.Failure(StatusCodes.Status400BadRequest, "End date should be after start date");

if (!skipStartDateEvaluation && startDate < _dateTimeService.GetCurrentDateTime())
return Result.Failure(StatusCodes.Status400BadRequest, "Start date is in the past");

if (ticketTypes.Any(t => t.AvailableFrom > endDate))
{
return Result.Failure(StatusCodes.Status400BadRequest, "Tickets can't be available after the event is over");
}

return Result.Success();
}
}
Loading