Adding functionality

This commit is contained in:
Tara Wilson 2024-12-19 15:58:37 -05:00
parent 6731c4e5f4
commit 6812f48c59
26 changed files with 267 additions and 72 deletions

View File

@ -56,15 +56,16 @@ public class EventController(IEventManager eventManager) : ControllerBase
/// </summary> /// </summary>
/// <param name="startDate">Start date for the event search</param> /// <param name="startDate">Start date for the event search</param>
/// <param name="endDate">End date for the event search</param> /// <param name="endDate">End date for the event search</param>
/// <param name="seasonId">Season to find events for</param>
/// <returns></returns> /// <returns></returns>
[HttpGet] [HttpGet]
public ActionResult<List<Event>> Get(DateTime? startDate, DateTime? endDate) public ActionResult<List<Event>> Get(DateTime? startDate, DateTime? endDate, string? seasonId)
{ {
try try
{ {
if (startDate == null && endDate == null) if (startDate == null && endDate == null)
{ {
return Ok(eventManager.GetAllEvents()); return Ok(seasonId == null ? eventManager.GetAllEvents() : eventManager.GetBySeason(new Guid(seasonId)));
} }
return Ok(eventManager.GetEvents(startDate, endDate)); return Ok(eventManager.GetEvents(startDate, endDate));
} }
@ -73,4 +74,24 @@ public class EventController(IEventManager eventManager) : ControllerBase
return BadRequest(e.Message); return BadRequest(e.Message);
} }
} }
/// <summary>
/// Deletes an event
/// </summary>
/// <param name="eventId">Event to delete</param>
/// <returns></returns>
[HttpDelete]
public IActionResult Delete(Guid eventId)
{
// TODO: Protect Endpoint
try
{
eventManager.DeleteEvent(eventId);
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
} }

View File

@ -53,18 +53,39 @@ public class SeasonController(ISeasonManager seasonManager) : ControllerBase
} }
/// <summary> /// <summary>
/// Adds an event to a season /// Updates a season
/// </summary> /// </summary>
/// <param name="seasonId">Season Id</param> /// <param name="request">Updated season information</param>
/// <param name="eventId">Event Id</param>
/// <returns></returns> /// <returns></returns>
[HttpPut] [HttpPatch]
public IActionResult Put(Guid eventId, Guid seasonId) public IActionResult Patch(PatchSeason request)
{ {
//TODO: Protect Endpoint //TODO: Protect Endpoint
try try
{ {
seasonManager.AddEventToSeason(eventId, seasonId); seasonManager.PatchSeason(request);
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
/// <summary>
/// Deletes a season
/// </summary>
/// <param name="seasonId">SeasonId to delete</param>
/// <returns></returns>
[HttpDelete]
public IActionResult Delete(Guid seasonId)
{
//TODO: Protect Endpoint
try
{
seasonManager.DeleteSeason(seasonId);
return Ok(); return Ok();
} }
catch (Exception e) catch (Exception e)

View File

@ -11,4 +11,6 @@ public interface IEventManager
List<Event> GetEvents(DateTime? startDate, DateTime? endDate); List<Event> GetEvents(DateTime? startDate, DateTime? endDate);
List<Event> GetAllEvents(); List<Event> GetAllEvents();
EventDetails GetEvent(Guid id); EventDetails GetEvent(Guid id);
void DeleteEvent(Guid eventId);
List<Event> GetBySeason(Guid seasonId);
} }

View File

@ -7,5 +7,6 @@ public interface ISeasonManager
{ {
void AddSeason(AddSeason season); void AddSeason(AddSeason season);
List<Season> GetSeasons(); List<Season> GetSeasons();
void AddEventToSeason(Guid eventId, Guid seasonId); void PatchSeason(PatchSeason request);
void DeleteSeason(Guid seasonId);
} }

View File

@ -63,3 +63,9 @@ Content-Type: application/json
GET {{api_HostAddress}}/event GET {{api_HostAddress}}/event
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
###
GET {{api_HostAddress}}/event?seasonId={{seasonId}}
Accept: application/json
Content-Type: application/json

View File

@ -18,7 +18,13 @@ public class EmailService(IConfiguration config) : IEmailService
config.GetSection("Email:Password").Value); config.GetSection("Email:Password").Value);
client.Credentials = auth; client.Credentials = auth;
var from = new MailAddress(config.GetSection("Email:From").Value); var from = new MailAddress(config.GetSection("Email:From").Value ?? string.Empty);
if (from.Address != config.GetSection("Email:From").Value)
{
throw new Exception("Invalid email address");
}
var to = new MailAddress(ticket.Patron.Email); var to = new MailAddress(ticket.Patron.Email);
var emailMessage = new MailMessage(from, to); var emailMessage = new MailMessage(from, to);

View File

@ -22,7 +22,6 @@ public class EventManager : IEventManager
}; };
new Save().Execute(@event); new Save().Execute(@event);
new data.Seasons.AddEvent().Execute(@event.Id, request.SeasonId);
} }
public void PatchEvent(PatchEvent request) public void PatchEvent(PatchEvent request)
@ -44,4 +43,14 @@ public class EventManager : IEventManager
{ {
return new GetDetails().Execute(id); return new GetDetails().Execute(id);
} }
public void DeleteEvent(Guid eventId)
{
new Delete().Execute(eventId);
}
public List<Event> GetBySeason(Guid seasonId)
{
return new GetBySeason().Execute(seasonId);
}
} }

View File

@ -2,7 +2,6 @@ using api.Interfaces;
using data.Seasons; using data.Seasons;
using models.Core; using models.Core;
using models.Request; using models.Request;
using AddEvent = data.Seasons.AddEvent;
namespace api.Services; namespace api.Services;
@ -27,8 +26,13 @@ public class SeasonManager : ISeasonManager
return new GetAll().Execute(); return new GetAll().Execute();
} }
public void AddEventToSeason(Guid eventId, Guid seasonId) public void PatchSeason(PatchSeason request)
{ {
new AddEvent().Execute(eventId, seasonId); new Update().Execute(request);
}
public void DeleteSeason(Guid seasonId)
{
new Delete().Execute(seasonId);
} }
} }

View File

@ -10,7 +10,6 @@ public class TicketManager : ITicketManager
public void SaveMintedTicket(Ticket ticket) public void SaveMintedTicket(Ticket ticket)
{ {
new data.Tickets.Save().Execute(ticket); new data.Tickets.Save().Execute(ticket);
new data.Events.AddTicket().Execute(ticket.Id, ticket.EventId);
} }
public TicketSearch SearchTicket(Guid ticketId) public TicketSearch SearchTicket(Guid ticketId)

View File

@ -3,16 +3,14 @@ using MongoDB.Driver;
namespace data.Events; namespace data.Events;
public class AddTicket public class Delete
{ {
public void Execute(Guid ticketId, Guid eventId) public void Execute(Guid eventId)
{ {
var database = MongoFactory.GetDatabase(); var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId); var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
var collection = database.GetCollection<Event>("events");
var update = Builders<Event>.Update.Push("TicketIds", ticketId); collection.DeleteOne(filter);
collection.FindOneAndUpdate(filter, update);
} }
} }

View File

@ -9,7 +9,7 @@ public class Find
{ {
var database = MongoFactory.GetDatabase(); var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events"); var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq("Id", eventId); var filter = Builders<Event>.Filter.Eq(e => e.Id, eventId);
return collection.Find(filter).FirstOrDefault(); return collection.Find(filter).FirstOrDefault();
} }
} }

View File

@ -0,0 +1,16 @@
using models.Core;
using MongoDB.Driver;
namespace data.Events;
public class GetBySeason
{
public List<Event> Execute(Guid seasonId)
{
var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq(e => e.SeasonId, seasonId);
return collection.Find(filter).ToList();
}
}

View File

@ -6,16 +6,17 @@ namespace data.Events;
public class Update public class Update
{ {
public bool Execute(PatchEvent request) public void Execute(PatchEvent request)
{ {
var database = MongoFactory.GetDatabase(); var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events"); var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq("_id", request.Id); var filter = Builders<Event>.Filter.Eq(e => e.Id, request.Id);
var newEvent = new Event var newEvent = new Event
{ {
Id = request.Id, Id = request.Id,
SeasonId = request.SeasonId,
Date = request.Date, Date = request.Date,
Name = request.Name, Name = request.Name,
Description = request.Description, Description = request.Description,
@ -23,6 +24,6 @@ public class Update
Talent = request.Talent, Talent = request.Talent,
}; };
return collection.ReplaceOne(filter, newEvent).IsAcknowledged; collection.ReplaceOne(filter, newEvent);
} }
} }

View File

@ -3,16 +3,14 @@ using MongoDB.Driver;
namespace data.Seasons; namespace data.Seasons;
public class AddEvent public class Delete
{ {
public void Execute(Guid eventId, Guid seasonId) public void Execute(Guid seasonId)
{ {
var database = MongoFactory.GetDatabase(); var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Season>("seasons");
var filter = Builders<Season>.Filter.Eq(s => s.Id, seasonId); var filter = Builders<Season>.Filter.Eq(s => s.Id, seasonId);
var collection = database.GetCollection<Season>("seasons");
var update = Builders<Season>.Update.Push("EventIds", eventId); collection.DeleteOne(filter);
collection.FindOneAndUpdate(filter, update);
} }
} }

View File

@ -0,0 +1,27 @@
using models.Core;
using models.Request;
using MongoDB.Driver;
namespace data.Seasons;
public class Update
{
public void Execute(PatchSeason request)
{
var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Season>("seasons");
var filter = Builders<Season>.Filter.Eq(s => s.Id, request.Id);
var newSeason = new Season
{
Id = request.Id,
Name = request.Name,
Description = request.Description,
StartDate = request.StartDate,
EndDate = request.EndDate,
};
collection.ReplaceOne(filter, newSeason);
}
}

View File

@ -9,5 +9,4 @@ public class Event
public string? Description { get; set; } public string? Description { get; set; }
public required Venue Venue { get; set; } public required Venue Venue { get; set; }
public required Talent Talent { get; set; } public required Talent Talent { get; set; }
public List<Guid> TicketIds { get; set; } = [];
} }

View File

@ -7,5 +7,4 @@ public class Season
public string? Description { get; set; } public string? Description { get; set; }
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
public List<Guid> EventIds { get; set; } = [];
} }

View File

@ -5,6 +5,7 @@ namespace models.Request;
public class PatchEvent public class PatchEvent
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid SeasonId { get; set; }
public DateTime Date { get; set; } public DateTime Date { get; set; }
public required string Name { get; set; } public required string Name { get; set; }
public string? Description { get; set; } public string? Description { get; set; }

View File

@ -0,0 +1,10 @@
namespace models.Request;
public class PatchSeason
{
public Guid Id { get; set; }
public required string Name { get; set; }
public string? Description { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}

View File

@ -17,7 +17,7 @@ export class EventBrowserComponent implements OnInit {
public selectedEventId$: WritableSignal<string> = signal(''); public selectedEventId$: WritableSignal<string> = signal('');
public ngOnInit() { public ngOnInit() {
this.eventService.searchAllEvents(); this.eventService.searchEvents(undefined, undefined, undefined);
} }
public click(eventId: string): void { public click(eventId: string): void {

View File

@ -1,31 +1,13 @@
import {HttpHeaders, HttpParams} from '@angular/common/http'; import {HttpHeaders} from '@angular/common/http';
export class ApiUtils { export class ApiUtils {
protected setHttpRequestOptions(params?: any): any { protected setHttpRequestOptions(): any {
let headers: HttpHeaders = new HttpHeaders(); let headers: HttpHeaders = new HttpHeaders();
headers = headers.set('Content-Type', 'application/json'); headers = headers.set('Content-Type', 'application/json');
const options: any = { return {
headers, headers,
observe: 'response' observe: 'response'
}; };
if (params) {
options.params = this.setHttpParams(params);
}
return options;
}
private setHttpParams(query: object): HttpParams {
const params = new HttpParams();
for (const key in query) {
// @ts-ignore
if (query[key] && query.hasOwnProperty(key)) {
// @ts-ignore
params.append(key, query[key]);
}
}
return params;
} }
} }

View File

@ -7,6 +7,7 @@ import {Endpoints} from '../../models/endpoints';
import {catchError, map, of} from 'rxjs'; import {catchError, map, of} from 'rxjs';
import {SnackbarService} from './snackbar.service'; import {SnackbarService} from './snackbar.service';
import {Event} from '../../models/core/event'; import {Event} from '../../models/core/event';
import {PatchEvent} from '../../models/request/patch-event';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -21,7 +22,6 @@ export class EventService extends ApiUtils {
} }
public addEvent(request: AddEventRequest): void { public addEvent(request: AddEventRequest): void {
//TODO: Remove hard coded venue and talent information
const options = this.setHttpRequestOptions(); const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.EVENT; const url = environment.apiBase + Endpoints.EVENT;
@ -34,40 +34,80 @@ export class EventService extends ApiUtils {
).subscribe(res => { ).subscribe(res => {
if(res !== null) { if(res !== null) {
this.snackbar.showMessage('Event added successfully.'); this.snackbar.showMessage('Event added successfully.');
this.searchAllEvents(); this.searchEvents(undefined, undefined, undefined);
} }
}); });
} }
public searchAllEvents(): void { /**
* Searches for events, returns all events if parameters are not provided
* @param startDate If provided with an end date, will search in the date range
* @param endDate If provided with a start date, will search in the date range
* @param seasonId If provided, returns all events for a season
*/
public searchEvents(startDate: Date | undefined,
endDate: Date | undefined,
seasonId: string | undefined): void {
const options = this.setHttpRequestOptions();
let url: string = '';
if (startDate && endDate) {
url = environment.apiBase + Endpoints.EVENT_DATE_SEARCH(startDate, endDate);
} else if (seasonId) {
url = environment.apiBase + Endpoints.EVENT_SEASON_SEARCH(seasonId);
} else {
url = environment.apiBase + Endpoints.EVENT;
}
this.httpClient.get(url, options)
.pipe(
map((response: any) => response.body),
catchError(error => {
this.snackbar.showError(error.error);
return of(undefined);
})
).subscribe(res => {
if(res !== null) {
this.events$.set(res);
} else {
this.snackbar.showError('No events found.');
}
});
}
public updateEvent(request: PatchEvent): void {
const options = this.setHttpRequestOptions(); const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.EVENT; const url = environment.apiBase + Endpoints.EVENT;
this.httpClient.get(url, options) this.httpClient.patch(url, JSON.stringify(request), options)
.pipe( .pipe(
map((response: any) => response.body),
catchError(error => { catchError(error => {
this.snackbar.showError(error.error); this.snackbar.showError(error.error);
return of(undefined); return of(undefined);
}) })
).subscribe(res => { ).subscribe(res => {
this.events$.set(res); if(res !== null) {
}); this.snackbar.showMessage('Event updated successfully.');
this.searchEvents(undefined, undefined, undefined);
}
})
} }
public searchEvents(startDate: Date, endDate: Date): void { public deleteEvent(eventId: string): void {
const options = this.setHttpRequestOptions(); const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.EVENT_DATE_SEARCH(startDate, endDate); const url = environment.apiBase + Endpoints.EVENT_DELETE(eventId);
this.httpClient.get(url, options) this.httpClient.delete(url, options)
.pipe( .pipe(
map((response: any) => response.body),
catchError(error => { catchError(error => {
this.snackbar.showError(error.error); this.snackbar.showError(error.error);
return of(undefined); return of(undefined);
}) })
).subscribe(res => { ).subscribe(res => {
this.events$.set(res); if(res !== null) {
}); this.snackbar.showMessage('Event deleted successfully.');
this.searchEvents(undefined, undefined, undefined);
}
})
} }
} }

View File

@ -7,6 +7,7 @@ import {environment} from '../../environments/environment';
import {Endpoints} from '../../models/endpoints'; import {Endpoints} from '../../models/endpoints';
import {catchError, map, of} from 'rxjs'; import {catchError, map, of} from 'rxjs';
import {AddSeason} from '../../models/request/add-season'; import {AddSeason} from '../../models/request/add-season';
import {PatchSeason} from '../../models/request/patch-season';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -39,7 +40,7 @@ export class SeasonService extends ApiUtils {
} }
public saveSeason(request: AddSeason): void { public saveSeason(request: AddSeason): void {
const options = this.setHttpRequestOptions(request); const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.SEASON; const url = environment.apiBase + Endpoints.SEASON;
this.httpClient.post(url, JSON.stringify(request), options) this.httpClient.post(url, JSON.stringify(request), options)
@ -68,4 +69,40 @@ export class SeasonService extends ApiUtils {
}) })
).subscribe(); ).subscribe();
} }
public updateSeason(request: PatchSeason): void {
const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.SEASON;
this.httpClient.patch(url, JSON.stringify(request), options)
.pipe(
catchError(error => {
this.snackbar.showError(error.error);
return of(undefined);
})
).subscribe(res => {
if (res !== null) {
this.snackbar.showMessage('Season updated successfully.');
this.getSeasons();
}
});
}
public deleteSeason(seasonId: string): void {
const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.SEASON_DELETE(seasonId);
this.httpClient.delete(url, options)
.pipe(
catchError(error => {
this.snackbar.showError(error.error);
return of(undefined);
})
).subscribe(res => {
if (res !== null) {
this.snackbar.showMessage('Season deleted successfully.');
this.getSeasons();
}
});
}
} }

View File

@ -8,6 +8,5 @@ export interface Event {
name: string, name: string,
description: string | null, description: string | null,
venue: Venue, venue: Venue,
talent: Talent, talent: Talent
ticketIds: Array<string>
} }

View File

@ -22,6 +22,14 @@ export class Endpoints {
return `${Endpoints.EVENT}?startDate=${startDate}&endDate=${endDate}`; return `${Endpoints.EVENT}?startDate=${startDate}&endDate=${endDate}`;
} }
public static EVENT_SEASON_SEARCH(seasonId: string): string {
return `${Endpoints.EVENT}?seasonId=${seasonId}`;
}
public static EVENT_DELETE(eventId: string): string {
return `${Endpoints.EVENT}?eventId=${eventId}`;
}
/* Season Routes */ /* Season Routes */
public static readonly SEASON: string = 'season'; public static readonly SEASON: string = 'season';
@ -29,4 +37,8 @@ export class Endpoints {
public static SEASON_ADD_EVENT(eventId: string, seasonId: string): string { public static SEASON_ADD_EVENT(eventId: string, seasonId: string): string {
return `${Endpoints.SEASON}?eventId=${eventId}&seasonId=${seasonId}`; return `${Endpoints.SEASON}?eventId=${eventId}&seasonId=${seasonId}`;
} }
public static SEASON_DELETE(seasonId: string): string {
return `${Endpoints.SEASON}?seasonId=${seasonId}`;
}
} }

View File

@ -0,0 +1,7 @@
export interface PatchSeason {
id: string,
name: string,
description: string | null,
startDate: Date,
endDate: Date
}