diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPost.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPost.cs new file mode 100644 index 00000000..b6c3c184 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPost.cs @@ -0,0 +1,10 @@ +using System; + +namespace api_cinema_challenge.DTO.Calls; + +public class CustomerPost +{ + public string Name { get; set; } + public string Email { get; set; } + public string Phone { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPut.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPut.cs new file mode 100644 index 00000000..c4af57af --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/CustomerPut.cs @@ -0,0 +1,10 @@ +using System; + +namespace api_cinema_challenge.DTO.Calls; + +public class CustomerPut +{ + public string Name { get; set; } + public string Email { get; set; } + public string Phone { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePost.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePost.cs new file mode 100644 index 00000000..144a7b5c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePost.cs @@ -0,0 +1,51 @@ +using System; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.DTO; + +/* +Create a new movie. Optionally, a screenings array can be provided to create screenings when the movie gets created. If no screenings array is provided, the movie should be created as normal. +Request Body schema: application/json +title +required + +string +rating +required + +string +description +required + +string +runtimeMins +required + +integer +Array of objects + +{ + + "title": "Dodgeball", + "rating": "PG-13", + "description": "The greatest movie ever made.", + "runtimeMins": 126, + "screenings": + +[ + + {} + ] + +} +*/ +public class MoviePost +{ + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public int RuntimeMins { get; set; } + public IEnumerable Screenings { get; set; } +} + diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePut.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePut.cs new file mode 100644 index 00000000..30ed8db0 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/MoviePut.cs @@ -0,0 +1,11 @@ +using System; + +namespace api_cinema_challenge.DTO.Calls; + +public class MoviePut +{ + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public int RuntimeMins { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/ScreeningPost.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/ScreeningPost.cs new file mode 100644 index 00000000..244c9e4a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/ScreeningPost.cs @@ -0,0 +1,10 @@ +using System; + +namespace api_cinema_challenge.DTO.Calls; + +public class ScreeningPost +{ + public int ScreenNumber { get; set; } + public int Capacity { get; set; } + public DateTime StartsAt { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Calls/TicketPost.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/TicketPost.cs new file mode 100644 index 00000000..3352ff71 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Calls/TicketPost.cs @@ -0,0 +1,8 @@ +using System; + +namespace api_cinema_challenge.DTO.Calls; + +public class TicketPost +{ + public int NumSeats { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Payload.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Payload.cs new file mode 100644 index 00000000..cfb598cd --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Payload.cs @@ -0,0 +1,15 @@ +using System; + +namespace api_cinema_challenge.DTO; + +public class Payload +{ + public string Message { get; set; } + public T Data { get; set; } + + public Payload(T data) + { + Message = "success"; + Data = data; + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Responses/CustomerDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/CustomerDTO.cs new file mode 100644 index 00000000..13703cc3 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/CustomerDTO.cs @@ -0,0 +1,13 @@ +using System; + +namespace api_cinema_challenge.DTO.Responses; + +public class CustomerDTO +{ + public int Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Responses/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/MovieDTO.cs new file mode 100644 index 00000000..f3f4f7d2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/MovieDTO.cs @@ -0,0 +1,14 @@ +using System; + +namespace api_cinema_challenge.DTO.Responses; + +public class MovieDTO +{ + public int Id { get; set; } + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public int RuntimeMins { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Responses/ScreeningDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/ScreeningDTO.cs new file mode 100644 index 00000000..0b726597 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/ScreeningDTO.cs @@ -0,0 +1,13 @@ +using System; + +namespace api_cinema_challenge.DTO.Responses; + +public class ScreeningDTO +{ + public int MovieId { get; set; } + public int ScreenNumber { get; set; } + public int Capacity { get; set; } + public DateTime StartsAt { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/Responses/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/TicketDTO.cs new file mode 100644 index 00000000..617ab930 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/Responses/TicketDTO.cs @@ -0,0 +1,13 @@ +using System; +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.DTO.Responses; + +public class TicketDTO +{ + + public int Id { get; set; } + public int NumSeats { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { 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..0fb011e4 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; namespace api_cinema_challenge.Data @@ -6,7 +7,7 @@ namespace api_cinema_challenge.Data public class CinemaContext : DbContext { private string _connectionString; - public CinemaContext(DbContextOptions options) : base(options) + public CinemaContext() { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; @@ -20,7 +21,17 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { - + + CinemaData cd = new CinemaData(); + modelBuilder.Entity().HasData(cd.Movies); + modelBuilder.Entity().HasData(cd.Customers); + modelBuilder.Entity().HasData(cd.Screenings); + modelBuilder.Entity().HasData(cd.Tickets); } + + 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/Data/CinemaData.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaData.cs new file mode 100644 index 00000000..27d3d8e1 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaData.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.Data; + +public class CinemaData +{ + public List Movies { get; set; } + public List Customers { get; set; } + public List Screenings { get; set; } + public List Tickets { get; set; } + + public CinemaData() + { + var now = new DateTime(2025, 01, 01, 0, 0, 0, DateTimeKind.Utc); + + Movies = new List + { + 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 + } + }; + + Customers = new List + { + 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 + } + }; + + Screenings = new List + { + 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 + } + }; + + Tickets = new List + { + 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 + } + }; + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/Seeder.cs b/api-cinema-challenge/api-cinema-challenge/Data/Seeder.cs new file mode 100644 index 00000000..a34a65f2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Data/Seeder.cs @@ -0,0 +1,106 @@ +using System; +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.Data; + +public static class Seeder +{ + public static async Task SeedCinemaData(this WebApplication app) + { + using(var db = new CinemaContext()) + { + if (!db.Movies.Any()) + { + var movie1 = new Movie { Title = "The Matrix", RuntimeMins = 120, Rating = "PG-13", Description = "A computer hacker learns about the true nature of reality." }; + var movie2 = new Movie { Title = "The Matrix Reloaded", RuntimeMins = 138, Rating = "R", Description = "The second installment of the Matrix trilogy." }; + var movie3 = new Movie { Title = "The Matrix Revolutions", RuntimeMins = 129, Rating = "R", Description = "The final installment of the Matrix trilogy." }; + + db.AddRange(movie1, movie2, movie3); + await db.SaveChangesAsync(); // Ensure IDs are assigned + + // Refresh movie instances with assigned IDs + var movies = db.Movies.ToList(); + movie1 = movies.First(m => m.Title == "The Matrix"); + movie2 = movies.First(m => m.Title == "The Matrix Reloaded"); + movie3 = movies.First(m => m.Title == "The Matrix Revolutions"); + + Console.WriteLine("Movies seeded."); + } + + if (!db.Screenings.Any()) + { + var movie1 = db.Movies.First(m => m.Title == "The Matrix"); + var movie2 = db.Movies.First(m => m.Title == "The Matrix Reloaded"); + var movie3 = db.Movies.First(m => m.Title == "The Matrix Revolutions"); + + db.Add(new Screening { MovieId = movie1.Id, StartsAt = DateTime.UtcNow.AddHours(1) }); + db.Add(new Screening { MovieId = movie1.Id, StartsAt = DateTime.UtcNow.AddHours(3) }); + db.Add(new Screening { MovieId = movie2.Id, StartsAt = DateTime.UtcNow.AddHours(2) }); + db.Add(new Screening { MovieId = movie2.Id, StartsAt = DateTime.UtcNow.AddHours(4) }); + db.Add(new Screening { MovieId = movie3.Id, StartsAt = DateTime.UtcNow.AddHours(5) }); + db.Add(new Screening { MovieId = movie3.Id, StartsAt = DateTime.UtcNow.AddHours(7) }); + await db.SaveChangesAsync(); + + Console.WriteLine("Screenings seeded."); + } + + if (!db.Customers.Any()) + { + var customer1 = new Customer { Name = "Alice", Email = "alice@example.com", Phone = "1234567890" }; + var customer2 = new Customer { Name = "Bob", Email = "bob@example.com", Phone = "0987654321" }; + + db.AddRange(customer1, customer2); + await db.SaveChangesAsync(); + + // Refresh customer instances with assigned IDs + var customers = db.Customers.ToList(); + customer1 = customers.First(c => c.Name == "Alice"); + customer2 = customers.First(c => c.Name == "Bob"); + + Console.WriteLine("Customers seeded."); + + if (!db.Tickets.Any()) + { + var movie1 = db.Movies.First(m => m.Title == "The Matrix"); + var movie2 = db.Movies.First(m => m.Title == "The Matrix Reloaded"); + var movie3 = db.Movies.First(m => m.Title == "The Matrix Revolutions"); + + var screening1 = db.Screenings.First(s => s.MovieId == movie1.Id); + var screening2 = db.Screenings.First(s => s.MovieId == movie2.Id); + var screening3 = db.Screenings.First(s => s.MovieId == movie3.Id); + + db.Add(new Ticket { NumSeats = 2, ScreeningId = screening1.Id, CustomerId = customer1.Id }); + db.Add(new Ticket { NumSeats = 3, ScreeningId = screening2.Id, CustomerId = customer1.Id }); + db.Add(new Ticket { NumSeats = 1, ScreeningId = screening3.Id, CustomerId = customer2.Id }); + db.Add(new Ticket { NumSeats = 4, ScreeningId = screening3.Id, CustomerId = customer2.Id }); + await db.SaveChangesAsync(); + + Console.WriteLine("Tickets seeded."); + } + } + } + } + + public static async Task ResetCinemaData(this WebApplication app) + { + using (var db = new CinemaContext()) + { + db.Tickets.RemoveRange(db.Tickets); + db.Screenings.RemoveRange(db.Screenings); + db.Movies.RemoveRange(db.Movies); + db.Customers.RemoveRange(db.Customers); + + await db.SaveChangesAsync(); + } + } + + public static async Task RecreateAndSeedDatabase(this WebApplication app) + { + using (var db = new CinemaContext()) + { + await db.Database.EnsureDeletedAsync(); + await db.Database.EnsureCreatedAsync(); + await app.SeedCinemaData(); + } + } +} 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..78308846 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoints.cs @@ -0,0 +1,14 @@ +using System; + +namespace api_cinema_challenge.Endpoints; + +public static class CinemaEndpoints +{ + public static void ConfigureEndpoints(this WebApplication app) + { + app.MapCustomerEndpoints(); + app.MapMovieEndpoints(); + app.MapScreeningEndpoints(); + app.MapTicketEndpoints(); + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs new file mode 100644 index 00000000..0050d385 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs @@ -0,0 +1,67 @@ +using System; +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.DTO.Responses; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using AutoMapper; + +namespace api_cinema_challenge.Endpoints; + +public static class CustomerEndpoints +{ + public static void MapCustomerEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/customers"); + + group.MapPost("/", CreateCustomer); + group.MapGet("/", GetAllCustomers); + group.MapPut("/{id}", UpdateCustomer); + group.MapDelete("/{id}", DeleteCustomer); + } + + public static async Task CreateCustomer(IRepository repository, CustomerPost customerPost, IMapper mapper) + { + var response = await repository.Create(mapper.Map(customerPost)); + return TypedResults.Created("Created", new Payload(mapper.Map(response))); + } + + public static async Task GetAllCustomers(IRepository repository, IMapper mapper) + { + try + { + var customers = await repository.GetAll(); + return TypedResults.Ok(new Payload>(mapper.Map>(customers))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } + + public static async Task UpdateCustomer(int id, IRepository repository, CustomerPut customerPut, IMapper mapper) + { + var customer = await repository.GetById(id); + if (customer == null) + { + return TypedResults.NotFound($"No entity with id {id} found"); + } + + mapper.Map(customerPut, customer); // Map customerPut to the existing customer entity + await repository.Update(customer); + return TypedResults.Ok(new Payload(mapper.Map(customer))); + } + + public static async Task DeleteCustomer(int id, IRepository repository, IMapper mapper) + { + try + { + var customer = await repository.Delete(id); + return TypedResults.Ok(new Payload(mapper.Map(customer))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs new file mode 100644 index 00000000..8d1bcf87 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs @@ -0,0 +1,81 @@ +using System; +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.DTO.Responses; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using AutoMapper; + +namespace api_cinema_challenge.Endpoints; + +public static class MovieEndpoints +{ + public static void MapMovieEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/movies"); + + group.MapPost("/", CreateMovie); + group.MapGet("/", GetAllMovies); + group.MapPut("/{id}", UpdateMovie); + group.MapDelete("/{id}", DeleteMovie); + } + + public static async Task CreateMovie(IRepository movieRepository, IRepository screeningRepository, MoviePost moviePost, IMapper mapper) + { + var movie = mapper.Map(moviePost); + var response = await movieRepository.Create(movie); + + if (moviePost.Screenings != null) + { + foreach (var screeningPost in moviePost.Screenings) + { + var screening = mapper.Map(screeningPost); + screening.MovieId = response.Id; + await screeningRepository.Create(screening); + } + } + + return TypedResults.Created("Created", new Payload(mapper.Map(response))); + } + + public static async Task GetAllMovies(IRepository repository, IMapper mapper) + { + try + { + var movies = await repository.GetAll(); + return TypedResults.Ok(new Payload>(mapper.Map>(movies))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } + + public static async Task UpdateMovie(int id, IRepository repository, MoviePut moviePut, IMapper mapper) + { + var movie = await repository.GetById(id); + if (movie == null) + { + return TypedResults.NotFound($"No entity with id {id} found"); + } + + mapper.Map(moviePut, movie); // Map moviePut to the existing movie entity + await repository.Update(movie); + return TypedResults.Ok(new Payload(mapper.Map(movie))); + } + + public static async Task DeleteMovie(int id, IRepository repository, IMapper mapper) + { + try + { + var movie = await repository.Delete(id); + return TypedResults.Ok(new Payload(mapper.Map(movie))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } + + +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs new file mode 100644 index 00000000..e45713b9 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs @@ -0,0 +1,41 @@ +using System; +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.DTO.Responses; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using AutoMapper; + +namespace api_cinema_challenge.Endpoints; + +public static class ScreeningEndpoints +{ + public static void MapScreeningEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/movies/{movieId}/screenings"); + + group.MapPost("/", CreateScreening); + group.MapGet("/", GetAllScreenings); + } + + public static async Task CreateScreening(int movieId, IRepository repository, ScreeningPost screeningPost, IMapper mapper) + { + var screening = mapper.Map(screeningPost); + screening.MovieId = movieId; + var response = await repository.Create(screening); + return TypedResults.Created("Created", new Payload(mapper.Map(response))); + } + + public static async Task GetAllScreenings(int movieId, IRepository repository, IMapper mapper) + { + try + { + var screenings = await repository.GetAllFilter(s => s.MovieId == movieId); + return TypedResults.Ok(new Payload>(mapper.Map>(screenings))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/TicketEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/TicketEndpoints.cs new file mode 100644 index 00000000..daa38724 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/TicketEndpoints.cs @@ -0,0 +1,47 @@ +using System; +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.DTO.Responses; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using AutoMapper; + +namespace api_cinema_challenge.Endpoints; + +public static class TicketEndpoints +{ + public static void MapTicketEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/customers/{customerId}/screenings/{screeningId}"); + + group.MapPost("/", BookTicket); + group.MapGet("/", GetAllTickets); + } + + public static async Task BookTicket(int customerId, int screeningId, IRepository ticketRepository, IRepository screeningRepository, IMapper mapper, TicketPost ticketPost) + { + var screening = await screeningRepository.GetById(screeningId); + if (screening == null) + { + return TypedResults.NotFound($"No screening with id {screeningId} found"); + } + + var ticket = mapper.Map(ticketPost); + + var response = await ticketRepository.Create(ticket); + return TypedResults.Created("Created", new Payload(mapper.Map(response))); + } + + public static async Task GetAllTickets(IRepository ticketRepository, IMapper mapper) + { + try + { + var tickets = await ticketRepository.GetAll(); + return TypedResults.Ok(new Payload>(mapper.Map>(tickets))); + } + catch (Exception e) + { + return TypedResults.NotFound(e.Message); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.Designer.cs new file mode 100644 index 00000000..c7946720 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.Designer.cs @@ -0,0 +1,421 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + [Migration("20250131091256_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text") + .HasColumnName("phone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("customers"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Email = "john.doe@example.com", + Name = "John Doe", + Phone = "123-456-7890", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Email = "jane.smith@example.com", + Name = "Jane Smith", + Phone = "098-765-4321", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Email = "emily.davis@example.com", + Name = "Emily Davis", + Phone = "555-123-4567", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Email = "michael.brown@example.com", + Name = "Michael Brown", + Phone = "444-567-8901", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Email = "sarah.johnson@example.com", + Name = "Sarah Johnson", + Phone = "222-345-6789", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("rating"); + + b.Property("RuntimeMins") + .HasColumnType("integer") + .HasColumnName("runtime_mins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("movies"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + 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.", + Rating = "PG-13", + RuntimeMins = 148, + Title = "Inception", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Description = "When the menace known as The Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.", + Rating = "PG-13", + RuntimeMins = 152, + Title = "The Dark Knight", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Description = "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.", + Rating = "PG-13", + RuntimeMins = 169, + Title = "Interstellar", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + Description = "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", + Rating = "R", + RuntimeMins = 142, + Title = "The Shawshank Redemption", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + 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.", + Rating = "PG-13", + RuntimeMins = 106, + Title = "Dunkirk", + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer") + .HasColumnName("screen_number"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("starts_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("screenings"); + + b.HasData( + new + { + Id = 1, + Capacity = 100, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + MovieId = 1, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 1, 31, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 2, + Capacity = 80, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + MovieId = 4, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 31, 12, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 3, + Capacity = 120, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + MovieId = 5, + ScreenNumber = 3, + StartsAt = new DateTime(2025, 2, 1, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 4, + Capacity = 90, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + MovieId = 3, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 2, 1, 14, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 5, + Capacity = 110, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + MovieId = 3, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 31, 15, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("NumSeats") + .HasColumnType("integer") + .HasColumnName("num_seats"); + + b.Property("ScreeningId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ScreeningId"); + + b.ToTable("tickets"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + CustomerId = 1, + NumSeats = 2, + ScreeningId = 1, + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + CustomerId = 2, + NumSeats = 1, + ScreeningId = 2, + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + CustomerId = 3, + NumSeats = 4, + ScreeningId = 3, + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + CustomerId = 4, + NumSeats = 3, + ScreeningId = 4, + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), + CustomerId = 5, + NumSeats = 2, + ScreeningId = 5, + UpdatedAt = new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany("Screenings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "Customer") + .WithMany("Tickets") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screening", "Screening") + .WithMany() + .HasForeignKey("ScreeningId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Screening"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Navigation("Screenings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.cs new file mode 100644 index 00000000..ff3f4bd0 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091256_InitialCreate.cs @@ -0,0 +1,185 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "customers", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "text", nullable: false), + email = table.Column(type: "text", nullable: false), + phone = table.Column(type: "text", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_customers", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "movies", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + title = table.Column(type: "text", nullable: false), + rating = table.Column(type: "text", nullable: false), + description = table.Column(type: "text", nullable: false), + runtime_mins = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_movies", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "screenings", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + starts_at = table.Column(type: "timestamp with time zone", nullable: false), + MovieId = table.Column(type: "integer", nullable: false), + screen_number = table.Column(type: "integer", nullable: false), + capacity = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_screenings", x => x.id); + table.ForeignKey( + name: "FK_screenings_movies_MovieId", + column: x => x.MovieId, + principalTable: "movies", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "tickets", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + num_seats = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp with time zone", nullable: false), + updated_at = table.Column(type: "timestamp with time zone", nullable: false), + ScreeningId = table.Column(type: "integer", nullable: false), + CustomerId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_tickets", x => x.id); + table.ForeignKey( + name: "FK_tickets_customers_CustomerId", + column: x => x.CustomerId, + principalTable: "customers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_tickets_screenings_ScreeningId", + column: x => x.ScreeningId, + principalTable: "screenings", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.InsertData( + table: "customers", + columns: new[] { "id", "created_at", "email", "name", "phone", "updated_at" }, + values: new object[,] + { + { 1, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "john.doe@example.com", "John Doe", "123-456-7890", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 2, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "jane.smith@example.com", "Jane Smith", "098-765-4321", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 3, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "emily.davis@example.com", "Emily Davis", "555-123-4567", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 4, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "michael.brown@example.com", "Michael Brown", "444-567-8901", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 5, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "sarah.johnson@example.com", "Sarah Johnson", "222-345-6789", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) } + }); + + migrationBuilder.InsertData( + table: "movies", + columns: new[] { "id", "created_at", "description", "rating", "runtime_mins", "title", "updated_at" }, + values: new object[,] + { + { 1, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "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.", "PG-13", 148, "Inception", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 2, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "When the menace known as The Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.", "PG-13", 152, "The Dark Knight", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 3, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.", "PG-13", 169, "Interstellar", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 4, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", "R", 142, "The Shawshank Redemption", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 5, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), "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.", "PG-13", 106, "Dunkirk", new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) } + }); + + migrationBuilder.InsertData( + table: "screenings", + columns: new[] { "id", "capacity", "created_at", "MovieId", "screen_number", "starts_at", "updated_at" }, + values: new object[,] + { + { 1, 100, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 1, 1, new DateTime(2025, 1, 31, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 2, 80, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 4, 2, new DateTime(2025, 1, 31, 12, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 3, 120, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 5, 3, new DateTime(2025, 2, 1, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 4, 90, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 3, 1, new DateTime(2025, 2, 1, 14, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 5, 110, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 3, 2, new DateTime(2025, 1, 31, 15, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) } + }); + + migrationBuilder.InsertData( + table: "tickets", + columns: new[] { "id", "created_at", "CustomerId", "num_seats", "ScreeningId", "updated_at" }, + values: new object[,] + { + { 1, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 1, 2, 1, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 2, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 2, 1, 2, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 3, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 3, 4, 3, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 4, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 4, 3, 4, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }, + { 5, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), 5, 2, 5, new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) } + }); + + migrationBuilder.CreateIndex( + name: "IX_screenings_MovieId", + table: "screenings", + column: "MovieId"); + + migrationBuilder.CreateIndex( + name: "IX_tickets_CustomerId", + table: "tickets", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_tickets_ScreeningId", + table: "tickets", + column: "ScreeningId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "tickets"); + + migrationBuilder.DropTable( + name: "customers"); + + migrationBuilder.DropTable( + name: "screenings"); + + migrationBuilder.DropTable( + name: "movies"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.Designer.cs new file mode 100644 index 00000000..bde25159 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.Designer.cs @@ -0,0 +1,421 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + [Migration("20250131091435_InitialCreate1")] + partial class InitialCreate1 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text") + .HasColumnName("phone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("customers"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "john.doe@example.com", + Name = "John Doe", + Phone = "123-456-7890", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "jane.smith@example.com", + Name = "Jane Smith", + Phone = "098-765-4321", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "emily.davis@example.com", + Name = "Emily Davis", + Phone = "555-123-4567", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "michael.brown@example.com", + Name = "Michael Brown", + Phone = "444-567-8901", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "sarah.johnson@example.com", + Name = "Sarah Johnson", + Phone = "222-345-6789", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("rating"); + + b.Property("RuntimeMins") + .HasColumnType("integer") + .HasColumnName("runtime_mins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("movies"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + 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.", + Rating = "PG-13", + RuntimeMins = 148, + Title = "Inception", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "When the menace known as The Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.", + Rating = "PG-13", + RuntimeMins = 152, + Title = "The Dark Knight", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.", + Rating = "PG-13", + RuntimeMins = 169, + Title = "Interstellar", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", + Rating = "R", + RuntimeMins = 142, + Title = "The Shawshank Redemption", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + 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.", + Rating = "PG-13", + RuntimeMins = 106, + Title = "Dunkirk", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer") + .HasColumnName("screen_number"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("starts_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("screenings"); + + b.HasData( + new + { + Id = 1, + Capacity = 100, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 1, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 1, 1, 2, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + Capacity = 80, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 4, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 1, 3, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + Capacity = 120, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 5, + ScreenNumber = 3, + StartsAt = new DateTime(2025, 1, 2, 2, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + Capacity = 90, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 3, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 1, 2, 5, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + Capacity = 110, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 3, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 1, 6, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("NumSeats") + .HasColumnType("integer") + .HasColumnName("num_seats"); + + b.Property("ScreeningId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ScreeningId"); + + b.ToTable("tickets"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 1, + NumSeats = 2, + ScreeningId = 1, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 2, + NumSeats = 1, + ScreeningId = 2, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 3, + NumSeats = 4, + ScreeningId = 3, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 4, + NumSeats = 3, + ScreeningId = 4, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 5, + NumSeats = 2, + ScreeningId = 5, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany("Screenings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "Customer") + .WithMany("Tickets") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screening", "Screening") + .WithMany() + .HasForeignKey("ScreeningId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Screening"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Navigation("Screenings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.cs new file mode 100644 index 00000000..8cb9e545 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250131091435_InitialCreate1.cs @@ -0,0 +1,299 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class InitialCreate1 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 2, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 3, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 2, 2, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 2, 5, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 6, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "customers", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "movies", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 12, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 2, 1, 11, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 2, 1, 14, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "screenings", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "starts_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 15, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 1, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 2, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 3, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 4, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + + migrationBuilder.UpdateData( + table: "tickets", + keyColumn: "id", + keyValue: 5, + columns: new[] { "created_at", "updated_at" }, + values: new object[] { new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532), new DateTime(2025, 1, 31, 9, 12, 55, 569, DateTimeKind.Utc).AddTicks(532) }); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs new file mode 100644 index 00000000..cd01d13a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,418 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + partial class CinemaContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("email"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text") + .HasColumnName("phone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("customers"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "john.doe@example.com", + Name = "John Doe", + Phone = "123-456-7890", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "jane.smith@example.com", + Name = "Jane Smith", + Phone = "098-765-4321", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "emily.davis@example.com", + Name = "Emily Davis", + Phone = "555-123-4567", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "michael.brown@example.com", + Name = "Michael Brown", + Phone = "444-567-8901", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Email = "sarah.johnson@example.com", + Name = "Sarah Johnson", + Phone = "222-345-6789", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("rating"); + + b.Property("RuntimeMins") + .HasColumnType("integer") + .HasColumnName("runtime_mins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("movies"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + 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.", + Rating = "PG-13", + RuntimeMins = 148, + Title = "Inception", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "When the menace known as The Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.", + Rating = "PG-13", + RuntimeMins = 152, + Title = "The Dark Knight", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.", + Rating = "PG-13", + RuntimeMins = 169, + Title = "Interstellar", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.", + Rating = "R", + RuntimeMins = 142, + Title = "The Shawshank Redemption", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + 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.", + Rating = "PG-13", + RuntimeMins = 106, + Title = "Dunkirk", + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer") + .HasColumnName("screen_number"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("starts_at"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("screenings"); + + b.HasData( + new + { + Id = 1, + Capacity = 100, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 1, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 1, 1, 2, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + Capacity = 80, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 4, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 1, 3, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + Capacity = 120, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 5, + ScreenNumber = 3, + StartsAt = new DateTime(2025, 1, 2, 2, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + Capacity = 90, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 3, + ScreenNumber = 1, + StartsAt = new DateTime(2025, 1, 2, 5, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + Capacity = 110, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + MovieId = 3, + ScreenNumber = 2, + StartsAt = new DateTime(2025, 1, 1, 6, 0, 0, 0, DateTimeKind.Utc), + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("NumSeats") + .HasColumnType("integer") + .HasColumnName("num_seats"); + + b.Property("ScreeningId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ScreeningId"); + + b.ToTable("tickets"); + + b.HasData( + new + { + Id = 1, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 1, + NumSeats = 2, + ScreeningId = 1, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 2, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 2, + NumSeats = 1, + ScreeningId = 2, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 3, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 3, + NumSeats = 4, + ScreeningId = 3, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 4, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 4, + NumSeats = 3, + ScreeningId = 4, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }, + new + { + Id = 5, + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CustomerId = 5, + NumSeats = 2, + ScreeningId = 5, + UpdatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) + }); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany("Screenings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "Customer") + .WithMany("Tickets") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screening", "Screening") + .WithMany() + .HasForeignKey("ScreeningId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Screening"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Navigation("Screenings"); + }); +#pragma warning restore 612, 618 + } + } +} 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..3494ba57 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models; + +/** + * "data": { + + "id": 1, + "name": "Chris Wolstenholme", + "email": "chris@muse.mu", + "phone": "+44729388192", + "createdAt": "2023-03-14T11:01:56.633+00:00", + "updatedAt": "2023-03-14T11:01:56.633+00:00" + +} + */ +[Table("customers")] +public class Customer +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("name")] + public string Name { get; set; } + + [Column("email")] + public string Email { get; set; } + + [Column("phone")] + public string Phone { get; set; } + + [Column("created_at")] + public DateTime CreatedAt { get; set; } + + [Column("updated_at")] + public DateTime UpdatedAt { get; set; } + + public IEnumerable Tickets { get; set; } +} \ No newline at end of file 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..67b44a4a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models; +/* +"data": { + + "id": 1, + "title": "Dodgeball", + "rating": "PG-13", + "description": "The greatest movie ever made.", + "runtimeMins": 126, + "createdAt": "2023-03-14T11:01:56.633+00:00", + "updatedAt": "2023-03-14T11:01:56.633+00:00" + +} +*/ +[Table("movies")] +public class Movie +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("title")] + public string Title { get; set; } + + [Column("rating")] + public string Rating { get; set; } + + [Column("description")] + public string Description { get; set; } + + [Column("runtime_mins")] + public int RuntimeMins { get; set; } + + [Column("created_at")] + public DateTime CreatedAt { get; set; } + + [Column("updated_at")] + public DateTime UpdatedAt { get; set; } + + public IEnumerable 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..959dbdff --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models; + +/* +"data": { + + "id": 1, + "screenNumber": 5, + "capacity": 40, + "startsAt": "2023-03-19T11:30:00.000+00:00", + "createdAt": "2023-03-14T11:01:56.633+00:00", + "updatedAt": "2023-03-14T11:01:56.633+00:00" + +} +*/ + +[Table("screenings")] +public class Screening +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("starts_at")] + public DateTime StartsAt { get; set; } + + [ForeignKey("Movie")] + public int MovieId { get; set; } + + public Movie Movie { get; set; } + + [Column("screen_number")] + public int ScreenNumber { get; set; } + + [Column("capacity")] + public int Capacity { get; set; } + + [Column("created_at")] + public DateTime CreatedAt { get; set; } + + [Column("updated_at")] + 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..a94e6585 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -0,0 +1,41 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models; + +/* +"data": { + + "id": 1, + "numSeats": 3, + "createdAt": "2023-03-14T11:01:56.633+00:00", + "updatedAt": "2023-03-14T11:01:56.633+00:00" + +} +*/ +[Table("tickets")] +public class Ticket +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("num_seats")] + public int NumSeats { get; set; } + + [Column("created_at")] + public DateTime CreatedAt { get; set; } + + [Column("updated_at")] + public DateTime UpdatedAt { get; set; } + + [ForeignKey("Screening")] + public int ScreeningId { get; set; } + public Screening Screening { get; set; } + + [ForeignKey("Customer")] + public int CustomerId { get; set; } + + public Customer Customer { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..12027ada 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,11 +1,25 @@ using api_cinema_challenge.Data; +using api_cinema_challenge.Endpoints; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; var builder = WebApplication.CreateBuilder(args); +builder.Services.AddControllers(); + +builder.Services.AddDbContext(); + // Add services to the container. builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -builder.Services.AddDbContext(); + +// scope repositories +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); + +builder.Services.AddAutoMapper(typeof(Program)); var app = builder.Build(); @@ -16,5 +30,11 @@ app.UseSwaggerUI(); } +app.UseAuthorization(); +app.MapControllers(); +// seed data +//await app.SeedCinemaData(); +//await app.ResetCinemaData(); +app.ConfigureEndpoints(); app.UseHttpsRedirection(); 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..1730bb60 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq.Expressions; + +namespace api_cinema_challenge.Repository; + +public interface IRepository where T : class +{ + Task> GetAll(); + Task GetById(int id); + Task Create(T entity); + Task Update(T entity); + Task Delete(int id); + Task> GetWithIncludes(params Expression>[] includes); + Task> GetAllFilter(Expression> filter); +} 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..a36b896d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq.Expressions; +using api_cinema_challenge.Data; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Repository; + +public class Repository : IRepository where T : class +{ + private readonly CinemaContext _context; + private DbSet _entities; + + public Repository(CinemaContext context) + { + _context = context; + _entities = context.Set(); + } + + public async Task> GetAll() + { + return await _entities.ToListAsync() ?? throw new KeyNotFoundException("No data found"); + } + + public async Task> GetAllFilter(Expression> filter) + { + return await _entities.Where(filter).ToListAsync() ?? throw new KeyNotFoundException("No data found"); + } + + public async Task GetById(int id) + { + return await _entities.FindAsync(id) ?? throw new KeyNotFoundException($"No entity with id {id} found"); + } + + public async Task Create(T entity) + { + _entities.Add(entity); + await Save(); + return entity; + } + + public async Task Update(T entity) + { + _entities.Update(entity); + _context.Entry(entity).State = EntityState.Modified; + await Save(); + return entity; + } + + public async Task Delete(int id) + { + var entity = GetById(id).Result; + _entities.Remove(entity); + await Save(); + return entity; + } + + public async Task> GetWithIncludes(params Expression>[] includes) + { + IQueryable query = _entities; + foreach (var include in includes) + { + query = query.Include(include); + } + return await query.ToListAsync(); + } + + public async Task Save() + { + await _context.SaveChangesAsync(); + } +} + diff --git a/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs b/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs new file mode 100644 index 00000000..8d90dc57 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs @@ -0,0 +1,31 @@ +using System; +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.Calls; +using api_cinema_challenge.DTO.Responses; +using api_cinema_challenge.Models; +using AutoMapper; + +namespace api_cinema_challenge.Tools; +public class MappingProfile : Profile +{ + public MappingProfile() + { + // Customer mappings + CreateMap(); + CreateMap(); + CreateMap(); + + // Movie mappings + CreateMap(); + CreateMap(); + CreateMap(); + + // Screening mappings + CreateMap(); + CreateMap(); + + // Ticket mappings + CreateMap(); + CreateMap(); + } +} \ No newline at end of file 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..d9d19bff 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -15,8 +15,13 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + all @@ -27,8 +32,4 @@ - - - - diff --git a/api-cinema-challenge/api-cinema-challenge/appsettings.example.json b/api-cinema-challenge/api-cinema-challenge/appsettings.example.json deleted file mode 100644 index b9175fe6..00000000 --- a/api-cinema-challenge/api-cinema-challenge/appsettings.example.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "DefaultConnectionString": "Host=HOST; Database=DATABASE; Username=USERNAME; Password=PASSWORD;" - } -} \ No newline at end of file diff --git a/api-cinema-challenge/populate_db.sql b/api-cinema-challenge/populate_db.sql new file mode 100644 index 00000000..6fa4bcfd --- /dev/null +++ b/api-cinema-challenge/populate_db.sql @@ -0,0 +1,30 @@ +-- Ensure tables are empty before inserting data +TRUNCATE TABLE tickets, screenings, movies, customers RESTART IDENTITY CASCADE; + +-- Populating movies table with created_at and updated_at values +INSERT INTO movies (id, title, rating, description, runtime_mins, created_at, updated_at) +VALUES +(1, 'The Matrix', 'PG-13', 'A computer hacker learns about the true nature of reality.', 120, NOW(), NOW()), +(2, 'The Matrix Reloaded', 'R', 'The second installment of the Matrix trilogy.', 138, NOW(), NOW()), +(3, 'The Matrix Revolutions', 'R', 'The final installment of the Matrix trilogy.', 129, NOW(), NOW()); + +-- Populating screenings table with correct foreign keys +INSERT INTO screenings (id, starts_at, movie_id, screen_number, capacity, created_at, updated_at) +VALUES +(1, '2025-01-31 09:48:29.054453+00', 1, 1, 40, NOW(), NOW()), +(2, '2025-01-31 11:48:29.054453+00', 2, 1, 40, NOW(), NOW()), +(3, '2025-01-31 13:48:29.054453+00', 3, 1, 40, NOW(), NOW()); + +-- Populating customers table with created_at and updated_at values +INSERT INTO customers (id, name, email, phone, created_at, updated_at) +VALUES +(1, 'Alice', 'alice@example.com', '1234567890', NOW(), NOW()), +(2, 'Bob', 'bob@example.com', '0987654321', NOW(), NOW()); + +-- Populating tickets table with correct foreign keys +INSERT INTO tickets (id, num_seats, screening_id, customer_id, created_at, updated_at) +VALUES +(1, 2, 1, 1, NOW(), NOW()), +(2, 3, 2, 1, NOW(), NOW()), +(3, 1, 3, 2, NOW(), NOW()), +(4, 4, 3, 2, NOW(), NOW());