Building out App

This commit is contained in:
Tara Wilson 2024-12-04 15:57:49 -05:00
parent a510abd7f7
commit da24314614
22 changed files with 228 additions and 23 deletions

View File

@ -60,4 +60,25 @@ public class TicketController(
return BadRequest(e.Message);
}
}
/// <summary>
/// Searches for a ticket with a given ticketId and validates it against the event associated with it.
/// </summary>
/// <param name="ticketId">A string representing a GUID value</param>
/// <returns>Ticket Search Result</returns>
[HttpGet]
public ActionResult<TicketSearch> Get(string ticketId)
{
//TODO: Protect Endpoint
try
{
var result = ticketManager.SearchTicket(ticketId);
return Ok(result);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
}

View File

@ -1,8 +1,11 @@
using models.Core;
using models.Response;
namespace api.Interfaces;
public interface ITicketManager
{
void SaveMintedTicket(Ticket ticket);
TicketSearch SearchTicket(string ticketId);
}

View File

@ -1,4 +1,5 @@
@api_HostAddress = http://localhost:5168
@eventId = b9f4478b-701b-4223-9aaa-042b6f53b83a
POST {{api_HostAddress}}/event
Accept: application/json
@ -30,8 +31,8 @@ Accept: application/json
Content-Type: application/json
{
"Id": "1a06c032-b073-4715-9b95-9f3410e7abd9",
"Date": "2024-12-03T15:00:00.991Z",
"Id": "{{eventId}}",
"Date": "2024-12-05T15:00:00.991Z",
"EventName": "Winter Concert",
"EventDescription": "A wintery journey of classical music",
"Venue": {

View File

@ -1,4 +1,6 @@
@api_HostAddress = http://localhost:5168
@ticketId = 59ae05ea-fd07-481a-9a6b-663444cc426d
@eventId = b9f4478b-701b-4223-9aaa-042b6f53b83a
POST {{api_HostAddress}}/ticket
Accept: application/json
@ -6,7 +8,11 @@ Content-Type: application/json
{
"ticketType": "Single",
"eventId": "1a06c032-b073-4715-9b95-9f3410e7abd9"
"eventId": "{{eventId}}"
}
###
GET {{api_HostAddress}}/ticket?ticketId={{ticketId}}
Accept: application/json
Content-Type: application/json

View File

@ -1,6 +1,7 @@
using api.Interfaces;
using data.Tickets;
using models.Core;
using models.Enumerations;
using models.Response;
namespace api.Services;
@ -8,7 +9,49 @@ public class TicketManager : ITicketManager
{
public void SaveMintedTicket(Ticket ticket)
{
//TODO: Add Ticket to Event
new Save().Execute(ticket);
new data.Tickets.Save().Execute(ticket);
new data.Events.AddTicket().Execute(ticket.Id, ticket.EventId);
}
public TicketSearch SearchTicket(string ticketId)
{
var ticket = new data.Tickets.Find().Execute(ticketId);
if (ticket == null)
{
throw new Exception("Ticket not found");
}
var @event = new data.Events.Find().Execute(ticket.EventId);
if (@event == null)
{
throw new Exception("Event not found");
}
var result = new TicketSearch
{
Type = ticket.Type,
Validity = DetermineValidity(@event)
};
return result;
}
private static TicketValidity DetermineValidity(Event @event)
{
if (@event.Date < DateTime.Now)
{
return TicketValidity.Expired;
}
if (@event.Date != DateTime.Today) return TicketValidity.Invalid;
if (DateTime.Now.Hour < @event.Date.Hour - 2)
{
return TicketValidity.Early;
}
return DateTime.Now.Hour > @event.Date.Hour + 5 ? TicketValidity.Expired : TicketValidity.Valid;
}
}

View File

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

View File

@ -0,0 +1,15 @@
using models.Core;
using MongoDB.Driver;
namespace data.Events;
public class Find
{
public Event Execute(Guid eventId)
{
var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq("Id", eventId);
return collection.Find(filter).FirstOrDefault();
}
}

View File

@ -11,7 +11,7 @@ public class Update
var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Event>("events");
var filter = Builders<Event>.Filter.Eq(e => e.Id, request.Id);
var filter = Builders<Event>.Filter.Eq("_id", request.Id);
var newEvent = new Event
{

View File

@ -1,4 +1,7 @@
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver;
namespace data;
@ -13,6 +16,8 @@ public static class MongoFactory
/// <param name="config"></param>
public static void InitConfig(IConfiguration config)
{
//set up the use of guids globally for the mongo connection
BsonSerializer.RegisterSerializer(new GuidSerializer(GuidRepresentation.Standard));
_config = config;
}

View File

@ -0,0 +1,15 @@
using models.Core;
using MongoDB.Driver;
namespace data.Tickets;
public class Find
{
public Ticket Execute(string ticketId)
{
var database = MongoFactory.GetDatabase();
var collection = database.GetCollection<Ticket>("tickets");
var filter = Builders<Ticket>.Filter.Eq("Id", ticketId);
return collection.Find(filter).FirstOrDefault();
}
}

View File

@ -1,11 +1,7 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace models.Core;
public class Event
{
[BsonGuidRepresentation(GuidRepresentation.Standard)]
public Guid Id { get; set; }
public DateTime Date { get; set; }
public required string EventName { get; set; }

View File

@ -1,6 +1,3 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace models.Core;
public class Talent

View File

@ -1,14 +1,10 @@
using models.Enumerations;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace models.Core;
public class Ticket
{
[BsonGuidRepresentation(GuidRepresentation.Standard)]
public Guid Id { get; set; }
[BsonGuidRepresentation(GuidRepresentation.Standard)]
public Guid EventId { get; set; }
public TicketType Type { get; set; }
public required string QrCode { get; set; }

View File

@ -0,0 +1,9 @@
namespace models.Enumerations;
public enum TicketValidity
{
Valid,
Expired,
Early,
Invalid
}

View File

@ -0,0 +1,9 @@
using models.Enumerations;
namespace models.Response;
public class TicketSearch
{
public TicketType Type { get; set; }
public TicketValidity Validity { get; set; }
}

View File

@ -2,4 +2,5 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConnectionString_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fb1a941e9ba91a5a4d64a4ebc59b771c12c16aeea8b9c9da3f70b88f93ae4d7_003FConnectionString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointRoutingApplicationBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F929ca880328a40769fd3ef8963dc5388e5400_003F20_003F918ae4ba_003FEndpointRoutingApplicationBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnsure_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Ffea93ee2ea48db69df97b9196fe77e86f2428c362835f6eda284e2df33bc5db_003FEnsure_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFieldDefinition_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2b48dfdf26ab2a566afcd1aceb264cd8416dabae58c6f655499d62d8119690_003FFieldDefinition_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Ffe3b1716bf2f8f83ac793e7e3faa60f88a7868347653f77591f74b86d0d97675_003FServer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@ -1,4 +1,4 @@
import {inject, Injectable} from '@angular/core';
import {inject, Injectable, signal, WritableSignal} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AddEventRequest} from '../../models/request/add-event-request';
import {ApiUtils} from './api-utils';
@ -10,6 +10,7 @@ import {catchError, of} from 'rxjs';
providedIn: 'root'
})
export class EventService extends ApiUtils {
public eventSignal: WritableSignal<Event | null> = signal(null);
private httpClient = inject(HttpClient);
constructor() {
@ -45,4 +46,17 @@ export class EventService extends ApiUtils {
})
).subscribe();
}
public searchEvents(startDate: Date, endDate: Date): void {
const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.EVENT;
this.httpClient.get(url, options)
.pipe(
catchError(error => {
console.log(error);
return of(undefined);
})
).subscribe();
}
}

View File

@ -1,4 +1,4 @@
import {inject, Injectable} from '@angular/core';
import {inject, Injectable, WritableSignal} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {signal} from '@angular/core';
import {catchError, map, of} from 'rxjs';
@ -7,12 +7,14 @@ import {MintResponse} from '../../models/response/mint-response';
import {Endpoints} from '../../models/endpoints';
import {MintRequest} from '../../models/request/mint-request';
import {ApiUtils} from './api-utils';
import {TicketValidity} from '../../models/enums/ticket-validity.enum';
@Injectable({
providedIn: 'root'
})
export class TicketService extends ApiUtils {
public dataSignal = signal('');
public dataSignal: WritableSignal<string> = signal('');
public validitySignal: WritableSignal<TicketValidity | null> = signal(null);
private httpClient = inject(HttpClient);
constructor() {
@ -36,4 +38,22 @@ export class TicketService extends ApiUtils {
}
});
}
public searchTicket(ticketId: string): void {
const options = this.setHttpRequestOptions();
const url = environment.apiBase + Endpoints.TICKET_SEARCH(ticketId);
this.httpClient.get<TicketValidity>(url, options)
.pipe(
map((response: any) => response.body),
catchError(error => {
console.log(error);
return of(undefined);
})
).subscribe((res: TicketValidity) => {
if (res !== undefined) {
this.validitySignal.set(res);
}
});
}
}

View File

@ -1,5 +1,17 @@
export class Endpoints {
public static readonly TICKET = 'ticket';
/* Ticket Routes */
public static readonly TICKET: string = 'ticket';
public static readonly EVENT = 'events';
/* Calculated Routes */
public static TICKET_SEARCH(ticketId: string): string {
return `${Endpoints.TICKET}?ticketId=${ticketId}`;
}
/* Event Routes */
public static readonly EVENT: string = 'events';
/* Calculated Routes */
public static EVENT_DATE_SEARCH(startDate: Date, endDate: Date): string {
return `${Endpoints.EVENT}?startDate=${startDate}&endDate=${endDate}`;
}
}

View File

@ -0,0 +1,6 @@
export enum TicketValidity {
Valid,
Expired,
Early,
Invalid
}

View File

@ -0,0 +1,11 @@
import {Venue} from '../core/venue';
import {Talent} from '../core/talent';
export interface PatchEvent {
id: string,
date: Date,
eventName: string,
eventDescription: string,
venue: Venue,
talent: Talent
}

View File

@ -0,0 +1,7 @@
import {TicketTypeEnum} from '../enums/ticket-type.enum';
import {TicketValidity} from '../enums/ticket-validity.enum';
export interface TicketSearch {
ticketType: TicketTypeEnum,
ticketValidity: TicketValidity
}