diff --git a/.gitignore b/.gitignore index cf332414..bc1605f1 100644 --- a/.gitignore +++ b/.gitignore @@ -368,3 +368,10 @@ FodyWeavers.xsd */**/bin/Release */**/obj/Debug */**/obj/Release +**/**/appsettings.json +**/**/appsettings.Development.json +**/**/bin/Debug +**/**/bin/Release +**/**/obj/Debug +**/**/obj/Release +/api-cinema-challenge/api-cinema-challenge/Migrations diff --git a/ERD.png b/ERD.png new file mode 100644 index 00000000..b26fa5e5 Binary files /dev/null and b/ERD.png differ diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs new file mode 100644 index 00000000..86057d12 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace api_cinema_challenge.DTOs +{ + public class CustomerDTO + { + public string Name { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs new file mode 100644 index 00000000..6bfdbc19 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs @@ -0,0 +1,17 @@ +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.DTOs +{ + public class MovieDTO + { + public string Title { get; set; } + + public string Rating { get; set; } + + public string Description { get; set; } + + public int RuntimeMins { get; set; } + + public List? Screenings { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs new file mode 100644 index 00000000..f03ed1df --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs @@ -0,0 +1,10 @@ +namespace api_cinema_challenge.DTOs +{ + public class ScreeningDTO + { + public int ScreenNumber { get; set; } + public int Capacity { get; set; } + public DateTime StartsAt { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs new file mode 100644 index 00000000..948b0f22 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs @@ -0,0 +1,8 @@ +namespace api_cinema_challenge.DTOs +{ + public class TicketDTO + { + public int NumSeats { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index ad4fe854..a217886a 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -1,5 +1,7 @@ -using Microsoft.EntityFrameworkCore; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; +using System.Diagnostics; namespace api_cinema_challenge.Data { @@ -16,11 +18,217 @@ public CinemaContext(DbContextOptions options) : base(options) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(_connectionString); + optionsBuilder.LogTo(message => Debug.WriteLine(message)); } protected override void OnModelCreating(ModelBuilder modelBuilder) { + var now = new DateTime(2025, 01, 01, 0, 0, 0, DateTimeKind.Utc); + modelBuilder.Entity().HasData( + new Movie + { + Id = 1, + Title = "Inception", + Rating = "PG-13", + Description = "A thief who steals corporate secrets through the use of dream-sharing technology is tasked with planting an idea into the mind of a C.E.O.", + RuntimeMins = 148, + CreatedAt = now, + UpdatedAt = now + }, + new Movie + { + Id = 2, + Title = "The Dark Knight", + Rating = "PG-13", + Description = "When the menace known as The Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.", + RuntimeMins = 152, + CreatedAt = now, + UpdatedAt = now + }, + new Movie + { + Id = 3, + Title = "Interstellar", + Rating = "PG-13", + Description = "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.", + RuntimeMins = 169, + CreatedAt = now, + UpdatedAt = now + }, + new Movie + { + Id = 4, + Title = "The Shawshank Redemption", + Rating = "R", + Description = "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", + RuntimeMins = 142, + CreatedAt = now, + UpdatedAt = now + }, + new Movie + { + Id = 5, + Title = "Dunkirk", + Rating = "PG-13", + Description = "Allied soldiers from Belgium, the British Empire, and France are surrounded by the German Army and evacuated during a fierce battle in World War II.", + RuntimeMins = 106, + CreatedAt = now, + UpdatedAt = now + } + ); + modelBuilder.Entity().HasData( + new Customer + { + Id = 1, + Name = "John Doe", + Email = "john.doe@example.com", + Phone = "123-456-7890", + CreatedAt = now, + UpdatedAt = now + }, + new Customer + { + Id = 2, + Name = "Jane Smith", + Email = "jane.smith@example.com", + Phone = "098-765-4321", + CreatedAt = now, + UpdatedAt = now + }, + new Customer + { + Id = 3, + Name = "Emily Davis", + Email = "emily.davis@example.com", + Phone = "555-123-4567", + CreatedAt = now, + UpdatedAt = now + }, + new Customer + { + Id = 4, + Name = "Michael Brown", + Email = "michael.brown@example.com", + Phone = "444-567-8901", + CreatedAt = now, + UpdatedAt = now + }, + new Customer + { + Id = 5, + Name = "Sarah Johnson", + Email = "sarah.johnson@example.com", + Phone = "222-345-6789", + CreatedAt = now, + UpdatedAt = now + } + ); + modelBuilder.Entity().HasData( + new Screening + { + Id = 1, + MovieId = 1, + ScreenNumber = 1, + Capacity = 100, + StartsAt = now.AddHours(2), + CreatedAt = now, + UpdatedAt = now + }, + new Screening + { + Id = 2, + MovieId = 4, + ScreenNumber = 2, + Capacity = 80, + StartsAt = now.AddHours(3), + CreatedAt = now, + UpdatedAt = now + }, + new Screening + { + Id = 3, + MovieId = 5, + ScreenNumber = 3, + Capacity = 120, + StartsAt = now.AddDays(1).AddHours(2), + CreatedAt = now, + UpdatedAt = now + }, + new Screening + { + Id = 4, + MovieId = 3, + ScreenNumber = 1, + Capacity = 90, + StartsAt = now.AddDays(1).AddHours(5), + CreatedAt = now, + UpdatedAt = now + }, + new Screening + { + Id = 5, + MovieId = 3, + ScreenNumber = 2, + Capacity = 110, + StartsAt = now.AddHours(6), + CreatedAt = now, + UpdatedAt = now + } + ); + modelBuilder.Entity().HasData( + new Ticket + { + Id = 1, + ScreeningId = 1, + CustomerId = 1, + NumSeats = 2, + CreatedAt = now, + UpdatedAt = now + }, + new Ticket + { + Id = 2, + ScreeningId = 2, + CustomerId = 2, + NumSeats = 1, + CreatedAt = now, + UpdatedAt = now + }, + new Ticket + { + Id = 3, + ScreeningId = 3, + CustomerId = 3, + NumSeats = 4, + CreatedAt = now, + UpdatedAt = now + }, + new Ticket + { + Id = 4, + ScreeningId = 4, + CustomerId = 4, + NumSeats = 3, + CreatedAt = now, + UpdatedAt = now + }, + new Ticket + { + Id = 5, + ScreeningId = 5, + CustomerId = 5, + NumSeats = 2, + CreatedAt = now, + UpdatedAt = now + } + ); } + + public DbSet Customers { get; set; } + public DbSet Movies { get; set; } + public DbSet Screenings { get; set; } + public DbSet Tickets { get; set; } + } } diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoints.cs new file mode 100644 index 00000000..4b1dc079 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoints.cs @@ -0,0 +1,195 @@ +using api_cinema_challenge.DTOs; +using api_cinema_challenge.Models; +using api_cinema_challenge.Payload; +using api_cinema_challenge.Repository; +using Microsoft.AspNetCore.Mvc; + +namespace api_cinema_challenge.Endpoints +{ + public static class CinemaEndpoints + { + public static void ConfigureCinemaEndpoint(this WebApplication app) + { + var movieGroup = app.MapGroup("movies"); + movieGroup.MapGet("/", GetMovies); + movieGroup.MapGet("/{id}", GetMovie); + movieGroup.MapPost("/", CreateMovie); + movieGroup.MapPut("/{id}", UpdateMovie); + movieGroup.MapDelete("/{id}", DeleteMovie); + //screenings for movies + movieGroup.MapPost("/{movieId}/screenings", CreateScreening); + movieGroup.MapGet("/{movieId}/screenings", GetScreenings); + + + + var customerGroup = app.MapGroup("customers"); + customerGroup.MapGet("/", GetCustomers); + customerGroup.MapGet("/{id}", GetCustomer); + customerGroup.MapPost("/", CreateCustomer); + customerGroup.MapPut("/{id}", UpdateCustomer); + customerGroup.MapDelete("/{id}", DeleteCustomer); + // tickets + customerGroup.MapPost("/{customerId}/screenings/{screeningId}", CreateTicket); + customerGroup.MapGet("/{customerId}/screenings/{screeningId}", GetTickets); + + + } + + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetMovies(IRepository repository) + { + var movies = await repository.GetMovies(); + return Results.Ok(await repository.GeneratePayload(movies)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetMovie(IRepository repository, int id) + { + var movie = await repository.GetMovie(id); + if (movie == null) + { + return TypedResults.NotFound(await repository.GenerateErrorPayload(movie, $"Movie with id {id} was not found")); + } + + var resp = await repository.GeneratePayload(movie); + + return Results.Ok(resp); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task CreateMovie(IRepository repository, MovieDTO? movie) + { + + var movieCreated = await repository.CreateMovie(movie); + return Results.Created($"movies/{movieCreated.Id}", await repository.GeneratePayload(movieCreated)); + } + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task UpdateMovie(IRepository repository, int id, MovieDTO? movie) + { + var updatedMovie = await repository.UpdateMovie(id, movie); + return Results.Created($"movies/{updatedMovie.Id}", await repository.GeneratePayload(updatedMovie)); + + } + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task DeleteMovie(IRepository repository, int id) + { + var deletedMovie = await repository.DeleteMovie(id); + if (deletedMovie == null) + { + return Results.NotFound(await repository.GenerateErrorPayload(deletedMovie, $"Movie with id {id} was not found")); + } + + return Results.Ok(await repository.GeneratePayload(deletedMovie)); + + } + + // Screenings for movies + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task CreateScreening(IRepository repository, int movieId, ScreeningDTO screeningDTO) + { + var movie = await repository.GetMovie(movieId); + if(movie == null) + { + return Results.NotFound(await repository.GenerateErrorPayload(movie, $"Movie with id {movieId} was not found")); + } + + + var screeningCreated = await repository.CreateScreening(screeningDTO, movieId); + return Results.Created($"/movies/{movieId}/screenings", await repository.GeneratePayload(screeningCreated)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetScreenings(IRepository repository, int movieId) + { + var screenings = await repository.GetScreenings(movieId); + return Results.Ok(await repository.GeneratePayload(screenings)); + + } + + + + // Customers + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetCustomers(IRepository repository) + { + var customers = await repository.GetCustomers(); + return Results.Ok(await repository.GeneratePayload(customers)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetCustomer(IRepository repository, int id) + { + var customer = await repository.GetCustomer(id); + if (customer == null) + { + return TypedResults.NotFound(await repository.GenerateErrorPayload(customer, $"Customer with id {id} was not found")); + } + + var resp = await repository.GeneratePayload(customer); + + return Results.Ok(resp); + } + + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task CreateCustomer(IRepository repository, CustomerDTO? customerDTO) + { + + var customerCreated = await repository.CreateCustomer(customerDTO); + return Results.Created($"/cinema/movies/{customerCreated.Id}", await repository.GeneratePayload(customerCreated)); + } + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task UpdateCustomer(IRepository repository, int id, CustomerDTO? customer) + { + var updatedCustomer = await repository.UpdateCustomer(id, customer); + return Results.Created($"/cinema/movies/{updatedCustomer.Id}", await repository.GeneratePayload(updatedCustomer)); + + } + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task DeleteCustomer(IRepository repository, int id) + { + var deletedCustomer = await repository.DeleteCustomer(id); + if (deletedCustomer == null) + { + return Results.NotFound(await repository.GenerateErrorPayload(deletedCustomer, $"Customer with id {id} was not found")); + } + + return Results.Ok(await repository.GeneratePayload(deletedCustomer)); + } + + // tickets for customers + [ProducesResponseType(StatusCodes.Status201Created)] + public static async Task CreateTicket(IRepository repository, int customerId, int screeningId, TicketDTO ticketDTO) + { + var customer = await repository.GetCustomer(customerId); + if (customer == null) + { + return Results.NotFound(await repository.GenerateErrorPayload(customer, $"Customer with id {customerId} was not found")); + } + + var screening = await repository.GetScreening(screeningId); + if (screening == null) + { + return Results.NotFound(await repository.GenerateErrorPayload(screening, $"Screening with id {screeningId} was not found")); + + } + + var ticket = await repository.CreateTicket(customer.Id, screening.Id, ticketDTO); + + return Results.Created($"/customers/{customerId}/screenings/{screeningId}", await repository.GeneratePayload(ticket)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetTickets(IRepository repository, int customerId, int screeningId) + { + var tickets = await repository.GetTickets(customerId, screeningId); + return Results.Ok(await repository.GeneratePayload(tickets)); + + } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs new file mode 100644 index 00000000..5520a7c6 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + [Tags("Customer")] + public class Customer + { + [Key] + public int Id { get; set; } + [Required] + [Column("Name")] + public string Name { get; set; } + [Required] + [Column("Email")] + public string Email { get; set; } + [Required] + [Column("Phone")] + public string Phone { get; set; } + + [Required] + [Column("CreatedAt")] + public DateTime CreatedAt { get; set; } + [Required] + [Column("UpdatedAt")] + public DateTime UpdatedAt { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs new file mode 100644 index 00000000..5c47851c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,36 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; + +namespace api_cinema_challenge.Models +{ + [Table("Movies")] + public class Movie + { + [Key] + public int Id { get; set; } + [Required] + [Column("Title")] + public string Title { get; set; } + [Required] + [Column("Rating")] + public string Rating { get; set; } + [Required] + [Column("Description")] + public string Description { get; set; } + [Required] + [Column("RuntimeMins")] + public int RuntimeMins { get; set; } + [Required] + [Column("CreatedAt")] + public DateTime CreatedAt { get; set; } + [Required] + [Column("UpdatedAt")] + public DateTime UpdatedAt { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + + public List? Screenings { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs new file mode 100644 index 00000000..939e4514 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + [Table("Screenings")] + public class Screening + { + [Key] + public int Id { get; set; } + [Required] + [ForeignKey("Movie")] + public int MovieId { get; set; } + [Required] + [Column("ScreenNumber")] + public int ScreenNumber { get; set; } + [Required] + [Column("Capacity")] + public int Capacity { get; set; } + [Required] + [Column("StartsAt")] + public DateTime StartsAt { get; set; } + [Required] + [Column("EndsAt")] + public DateTime CreatedAt { get; set; } + [Required] + [Column("CreatedAt")] + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs new file mode 100644 index 00000000..ecdf0651 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + [Table("Tickets")] + public class Ticket + { + [Key] + public int Id { get; set; } + [Required] + [ForeignKey("Screening")] + public int ScreeningId { get; set; } + [Required] + [ForeignKey("Customer")] + public int CustomerId { get; set; } + [Required] + [Column("SeatNumber")] + public int NumSeats { get; set; } + [Required] + [Column("CreatedAt")] + public DateTime CreatedAt { get; set; } + [Required] + [Column("UpdatedAt")] + public DateTime UpdatedAt { get; set; } + + public Customer customer { get; set; } + public Screening screening { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Payload/Payload.cs b/api-cinema-challenge/api-cinema-challenge/Payload/Payload.cs new file mode 100644 index 00000000..b30423cc --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Payload/Payload.cs @@ -0,0 +1,8 @@ +namespace api_cinema_challenge.Payload +{ + public class ApiResponse + { + public string Status { get; set; } + public T Data { get; set; } + } +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..ebee0238 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,4 +1,6 @@ using api_cinema_challenge.Data; +using api_cinema_challenge.Endpoints; +using api_cinema_challenge.Repository; var builder = WebApplication.CreateBuilder(args); @@ -6,6 +8,7 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(); +builder.Services.AddScoped(); var app = builder.Build(); @@ -17,4 +20,5 @@ } app.UseHttpsRedirection(); +app.ConfigureCinemaEndpoint(); app.Run(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs new file mode 100644 index 00000000..b3a937b1 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,38 @@ +using api_cinema_challenge.DTOs; +using api_cinema_challenge.Models; +using api_cinema_challenge.Payload; + +namespace api_cinema_challenge.Repository +{ + public interface IRepository + { + // Customers + Task> GetCustomers(); + Task GetCustomer(int id); + Task CreateCustomer(CustomerDTO customer); + Task UpdateCustomer(int id, CustomerDTO customer); + Task DeleteCustomer(int id); + + // Movies + Task> GetMovies(); // + Task GetMovie(int id); // + Task CreateMovie(MovieDTO movie); + Task UpdateMovie(int id, MovieDTO movie); + Task DeleteMovie(int id); + + // Screenings + Task?> GetScreenings(int id); + Task GetScreening(int id); + Task CreateScreening(ScreeningDTO? screening, int movieId); + + // Tickets + Task> GetTickets(int customerId, int screeningId); + Task CreateTicket(int customerId, int screeningId, TicketDTO ticketDTO); + + // MISC + Task> GeneratePayload(T data); + Task> GenerateErrorPayload(T data, string message); + + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs new file mode 100644 index 00000000..e4f412d9 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,214 @@ +using api_cinema_challenge.Data; +using api_cinema_challenge.DTOs; +using api_cinema_challenge.Models; +using api_cinema_challenge.Payload; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Repository +{ + public class Repository : IRepository + { + private CinemaContext _db; + + public Repository(CinemaContext db) + { + _db = db; + } + + + // CUSTOMERS _______________________________________________________ + public async Task> GetCustomers() + { + return await _db.Customers.ToListAsync(); + } + public async Task GetCustomer(int id) + { + return await _db.Customers.FirstOrDefaultAsync(c => c.Id == id); + } + + public async Task CreateCustomer(CustomerDTO customerDTO) + { + Customer customer = new Customer() + { + Name = customerDTO.Name, + Email = customerDTO.Email, + Phone = customerDTO.Phone, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + await _db.AddAsync(customer); + await _db.SaveChangesAsync(); + return customer; + } + public async Task UpdateCustomer(int id, CustomerDTO customer) + { + var customerToUpdate = await _db.Customers.FirstOrDefaultAsync(m => m.Id == id); + + if (customerToUpdate == null) + { + return null; + } + customerToUpdate.Name = customer.Name; + customerToUpdate.Email = customer.Email; + customerToUpdate.Phone = customer.Phone; + customerToUpdate.UpdatedAt = DateTime.UtcNow; + await _db.SaveChangesAsync(); + + return customerToUpdate; + } + + public async Task DeleteCustomer(int id) + { + var customerToDelete = await _db.Customers.FirstOrDefaultAsync(m => m.Id == id); + if (customerToDelete == null) + { + return null; + } + _db.Customers.Remove(customerToDelete); + await _db.SaveChangesAsync(); + return customerToDelete; + } + + // MOVIES _______________________________________________________ + + + public async Task> GetMovies() + { + return await _db.Movies.ToListAsync(); + } + public async Task GetMovie(int id) + { + return await _db.Movies.FirstOrDefaultAsync(m => m.Id == id); + } + public async Task CreateMovie(MovieDTO movieDTO) + { + Movie movie = new Movie() + { + Title = movieDTO.Title, + Rating = movieDTO.Rating, + RuntimeMins = movieDTO.RuntimeMins, + Description = movieDTO.Description, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + + await _db.AddAsync(movie); + await _db.SaveChangesAsync(); + + if (movieDTO.Screenings != null) + { + foreach (var s in movieDTO.Screenings) + { + await this.CreateScreening(s, movie.Id); + } + } + return movie; + } + public async Task UpdateMovie(int id, MovieDTO movie) + { + var movieToUpdate = await _db.Movies.FirstOrDefaultAsync(m => m.Id == id); + + if (movieToUpdate == null) + { + return null; + } + movieToUpdate.Title = movie.Title; + movieToUpdate.Rating = movie.Rating; + movieToUpdate.RuntimeMins = movie.RuntimeMins; + movieToUpdate.Description = movie.Description; + movieToUpdate.UpdatedAt = DateTime.UtcNow; + await _db.SaveChangesAsync(); + + return movieToUpdate; + } + public async Task DeleteMovie(int id) + { + var movieToDelete = await _db.Movies.FirstOrDefaultAsync(m => m.Id == id); + if(movieToDelete == null) + { + return null; + } + _db.Movies.Remove(movieToDelete); + await _db.SaveChangesAsync(); + return movieToDelete; + } + + // SCREENINGS _______________________________________________________ + public async Task?> GetScreenings(int movieId) + { + var movie = await this.GetMovie(movieId); + if(movie == null) + { + return null; + } + + return await _db.Screenings.Where(s => s.MovieId == movieId).ToListAsync(); + } + + public async Task GetScreening(int id) + { + return await _db.Screenings.FirstOrDefaultAsync(s => s.Id == id); + } + + public async Task CreateScreening(ScreeningDTO screeningDTO, int movieId) + { + Screening screening = new Screening() + { + ScreenNumber = screeningDTO.ScreenNumber, + Capacity = screeningDTO.Capacity, + StartsAt = screeningDTO.StartsAt, + MovieId = movieId, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + await _db.AddAsync(screening); + await _db.SaveChangesAsync(); + return screening; + } + + // TICKETS _______________________________________________________ + + + public async Task> GetTickets(int customerId, int screeningId) + { + return await _db.Tickets.Where(t => t.CustomerId == customerId && t.ScreeningId == screeningId).ToListAsync(); + } + + public async Task CreateTicket(int customerId, int screeningId, TicketDTO ticketDTO) + { + Ticket ticket = new Ticket() + { + CustomerId = customerId, + ScreeningId = screeningId, + NumSeats = ticketDTO.NumSeats, + CreatedAt = DateTime.UtcNow, + UpdatedAt= DateTime.UtcNow, + }; + + await _db.AddAsync(ticket); + await _db.SaveChangesAsync(); + + return ticket; + } + + // Payload _______________________________________________________ + public Task> GeneratePayload(T data) + { + return Task.FromResult(new ApiResponse + { + Status = "success", + Data = data + }); + } + public Task> GenerateErrorPayload(T data, string message) + { + return Task.FromResult(new ApiResponse + { + Status = message, + Data = data + }); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj index 8c888bf8..f10a597e 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -9,14 +9,23 @@ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + all @@ -24,11 +33,12 @@ + - +