diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs new file mode 100644 index 00000000..6bbda6ac --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs @@ -0,0 +1,14 @@ +namespace api_cinema_challenge.DTO +{ + 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/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs new file mode 100644 index 00000000..1216a7b3 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs @@ -0,0 +1,19 @@ +namespace api_cinema_challenge.DTO +{ + 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/ScreeningDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/ScreeningDTO.cs new file mode 100644 index 00000000..8952af00 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/ScreeningDTO.cs @@ -0,0 +1,18 @@ +namespace api_cinema_challenge.DTO +{ + namespace api_cinema_challenge.DTO + { + public class ScreeningDTO + { + public int Id { 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; } + public int MovieId { get; set; } + public string MovieTitle { 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..50cea177 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 @@ -10,7 +11,7 @@ public CinemaContext(DbContextOptions options) : base(options) { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; - this.Database.EnsureCreated(); + //this.Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -20,7 +21,135 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { + // Configuring the relationships and keys (if needed) + modelBuilder.Entity() + .HasOne(s => s.Movie) // One Screening is linked to one Movie + .WithMany() // Movie can have multiple Screenings + .HasForeignKey(s => s.MovieId); + // Optional: you can set up additional configurations here, e.g. table names, indexes, etc. } + + // DbSets for the models + public DbSet Screenings { get; set; } + public DbSet Movies { get; set; } + public DbSet Customers { get; set; } + + // Seeder method + public void SeedData() + { + if (!Movies.Any()) + { + Movies.AddRange( + new Movie + { + Title = "The Dark Knight", + Rating = "PG-13", + Description = "Batman faces the Joker, a criminal mastermind who wants to plunge Gotham into anarchy.", + RuntimeMins = 152, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Movie + { + Title = "Inception", + Rating = "PG-13", + Description = "A thief who enters the dreams of others to steal secrets from their subconscious is given the task of planting an idea into a CEO's mind.", + RuntimeMins = 148, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Movie + { + 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 = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + } + ); + + SaveChanges(); // ✅ Viktig! Lagre filmene før vi bruker dem i Screenings + } + + if (!Screenings.Any()) + { + var darkKnight = Movies.FirstOrDefault(m => m.Title == "The Dark Knight"); + var inception = Movies.FirstOrDefault(m => m.Title == "Inception"); + var interstellar = Movies.FirstOrDefault(m => m.Title == "Interstellar"); + + if (darkKnight == null || inception == null || interstellar == null) + { + return; // Feilsikring i tilfelle filmene ikke ble lagret + } + + Screenings.AddRange( + new Screening + { + ScreenNumber = 1, + Capacity = 100, + StartsAt = new DateTime(2025, 2, 1, 22, 30, 0, DateTimeKind.Utc), + MovieId = darkKnight.Id, // ✅ Nå vil dette fungere + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Screening + { + ScreenNumber = 2, + Capacity = 120, + StartsAt = new DateTime(2025, 2, 1, 19, 45, 0, DateTimeKind.Utc), + MovieId = inception.Id, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Screening + { + ScreenNumber = 3, + Capacity = 80, + StartsAt = new DateTime(2025, 2, 1, 18, 30, 0, DateTimeKind.Utc), + MovieId = interstellar.Id, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + } + ); + + SaveChanges(); + } + + if (!Customers.Any()) + { + Customers.AddRange( + new Customer + { + Name = "John Doe", + Email = "johndoe@example.com", + Phone = "+1234567890", + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Customer + { + Name = "Jane Smith", + Email = "janesmith@example.com", + Phone = "+0987654321", + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + }, + new Customer + { + Name = "Alice Johnson", + Email = "alicej@example.com", + Phone = "+1122334455", + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow, + } + ); + + SaveChanges(); + } + } + } } + diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoint.cs new file mode 100644 index 00000000..a3add492 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoint.cs @@ -0,0 +1,236 @@ + +using api_cinema_challenge.DTO; +using api_cinema_challenge.DTO.api_cinema_challenge.DTO; +using api_cinema_challenge.Repository; +using Microsoft.AspNetCore.Http.HttpResults; + +namespace api_cinema_challenge.Endpoints +{ + public static class MovieEndpoint + { + + public static void ConfigureCinemaEndpoint(this WebApplication app) + { + var cinema = app.MapGroup("Cinema"); + cinema.MapGet("/customers", GetCustomers); + cinema.MapPost("/customers", AddCustomers); + cinema.MapPut("/customers", UpdateCustomers); + cinema.MapDelete("/customers", DeleteCustomers); + //movies + cinema.MapGet("/movies", GetMovies); + cinema.MapPost("/movies", AddMovie); + cinema.MapPut("/movies", UpdateMovie); + cinema.MapDelete("/movies", DeleteMovie); + + //screenings + cinema.MapGet("/Screening", GetScreenings); + cinema.MapPost("/Screening", CreateScreening); + } + + private static async Task CreateScreening(IRepository repository, int movieId, int screenNumber, int capacity, DateTime startsAt) + { + var screening = await repository.CreateScreening(movieId, screenNumber, capacity, startsAt); + + if (screening == null) + { + return Results.BadRequest("Failed to create screening. Check movie ID or other input values."); + } + + var screeningDTO = new ScreeningDTO + { + Id = screening.Id, + ScreenNumber = screening.ScreenNumber, + Capacity = screening.Capacity, + StartsAt = screening.StartsAt, + CreatedAt = screening.CreatedAt, + UpdatedAt = screening.UpdatedAt, + MovieId = screening.MovieId, + MovieTitle = screening.Movie.Title + }; + + return TypedResults.Ok(screeningDTO); + } + + private static async Task GetScreenings(IRepository repository, int movieId) + { + var screenings = await repository.GetScreenings(movieId); + + if (screenings == null || !screenings.Any()) + { + return Results.NotFound("No screenings found for the given movie."); + } + + var screeningDTOs = screenings.Select(s => new ScreeningDTO + { + Id = s.Id, + ScreenNumber = s.ScreenNumber, + Capacity = s.Capacity, + StartsAt = s.StartsAt, + CreatedAt = s.CreatedAt, + UpdatedAt = s.UpdatedAt, + MovieId = s.MovieId, + MovieTitle = s.Movie.Title + }).ToList(); + + return TypedResults.Ok(screeningDTOs); + } + + + private static async Task DeleteMovie(IRepository repository, int id) + { + var movie = await repository.DeleteMovie(id); + if (movie == null) + { + return Results.NotFound(); // Return 404 if movie doesn't exist + } + + // You can return a confirmation message or a DTO if needed + return Results.Ok(new { Message = "Movie deleted successfully" }); + } + + private static async Task UpdateMovie(IRepository repository, int id, string title, string rating, string description, int runtimeMins) + { + var movie = await repository.UpdateMovie(id, title, rating, description, runtimeMins); + if (movie == null) + { + return Results.NotFound(); // Return 404 if movie doesn't exist + } + + MovieDTO dto = new MovieDTO + { + Id = movie.Id, + Title = movie.Title, + Rating = movie.Rating, + Description = movie.Description, + RuntimeMins = movie.RuntimeMins, + CreatedAt = movie.CreatedAt, + UpdatedAt = movie.UpdatedAt + }; + + return Results.Ok(dto); // Return updated movie details + } + + private static async Task AddMovie(IRepository repository, string title, string rating, string description, int runtimeMins) + { + var movie = await repository.AddMovie(title, rating, description, runtimeMins); + if (movie == null) + { + return Results.BadRequest("Movie could not be added"); // Return a 400 Bad Request if movie can't be added + } + + MovieDTO dto = new MovieDTO + { + Id = movie.Id, + Title = movie.Title, + Rating = movie.Rating, + Description = movie.Description, + RuntimeMins = movie.RuntimeMins, + CreatedAt = movie.CreatedAt, + UpdatedAt = movie.UpdatedAt + }; + + return Results.Ok(dto); // Return the added movie details + } + + private static async Task GetMovies(IRepository repository) + { + var movies = await repository.GetMovies(); + var movieDTO = movies.Select(m => new MovieDTO + { + + Id = m.Id, + Title = m.Title, + Rating = m.Rating, + Description = m.Description, + RuntimeMins=m.RuntimeMins, + CreatedAt = m.CreatedAt, + UpdatedAt = m.UpdatedAt, + + + }).ToList(); + + return TypedResults.Ok(movieDTO); + + } + + private static async Task DeleteCustomers(IRepository repository,int id) + { + var customer = await repository.DeleteCustomer(id); + CustomerDTO dto = new CustomerDTO(); + if (customer == null) + { + return Results.NotFound(); + } + dto.Id = customer.Id; + dto.Name = customer.Name; + dto.Email = customer.Email; + dto.Phone = customer.Phone; + dto.CreatedAt = customer.CreatedAt; + dto.UpdatedAt = customer.UpdatedAt; + + + return TypedResults.Ok(dto); + + + + } + + private static async Task UpdateCustomers(IRepository repository,int id, string name, string email, string phone ) + { + var customer= await repository.UpdateCustomer(id, name, email, phone); + CustomerDTO dto = new CustomerDTO(); + if (customer == null) + { + return Results.NotFound(); + } + dto.Id = customer.Id; + dto.Name = customer.Name; + dto.Email = customer.Email; + dto.Phone = customer.Phone; + dto.CreatedAt = customer.CreatedAt; + dto.UpdatedAt = customer.UpdatedAt; + + + return TypedResults.Ok(dto); + + + } + + private static async Task AddCustomers(IRepository repository, string name, string email,string phone) + { + var customer = await repository.AddCustomer(name, email, phone); + if (customer == null) + { + return Results.NotFound(); + } + CustomerDTO dto = new CustomerDTO(); + dto.Id = customer.Id; + dto.Name= customer.Name; + dto.Email= customer.Email; + dto.Phone= customer.Phone; + dto.CreatedAt = customer.CreatedAt; + dto.UpdatedAt = customer.UpdatedAt; + return TypedResults.Ok(dto); + } + + private static async Task GetCustomers(IRepository repository) + { + var customers = await repository.GetCustomers(); + var customerDTO = customers.Select(c => new CustomerDTO + { + + Id = c.Id, + Name = c.Name, + Email = c.Email, + Phone = c.Phone, + CreatedAt = c.CreatedAt, + UpdatedAt = c.UpdatedAt, + + + }).ToList(); + + return TypedResults.Ok(customerDTO); + + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.Designer.cs new file mode 100644 index 00000000..766d029b --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.Designer.cs @@ -0,0 +1,139 @@ +// +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("20250130074712_InitialMigration")] + partial class InitialMigration + { + /// + 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"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text"); + + b.Property("RuntimeMins") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.cs new file mode 100644 index 00000000..b3a18424 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250130074712_InitialMigration.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class InitialMigration : 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), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = 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), + RuntimeMins = table.Column(type: "integer", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = 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), + ScreenNumber = table.Column(type: "integer", nullable: false), + Capacity = table.Column(type: "integer", nullable: false), + StartsAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + MovieId = table.Column(type: "integer", 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.CreateIndex( + name: "IX_Screenings_MovieId", + table: "Screenings", + column: "MovieId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Customers"); + + migrationBuilder.DropTable( + name: "Screenings"); + + migrationBuilder.DropTable( + name: "Movies"); + } + } +} 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..a9d591a3 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,136 @@ +// +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"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text"); + + b.Property("RuntimeMins") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); +#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..d5b08f7b --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,13 @@ +namespace api_cinema_challenge.Models +{ + public class Customer + { + 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/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs new file mode 100644 index 00000000..f7499d75 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,17 @@ +namespace api_cinema_challenge.Models +{ + public class Movie + { + 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/Models/Screening.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs new file mode 100644 index 00000000..3c24e062 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs @@ -0,0 +1,19 @@ +namespace api_cinema_challenge.Models +{ + public class Screening + { + + public int Id { 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; } + + public int MovieId { get; set; } + + public Movie Movie { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..78a2a5c8 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,14 +1,30 @@ using api_cinema_challenge.Data; +using api_cinema_challenge.Endpoints; +using api_cinema_challenge.Repository; +using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -builder.Services.AddDbContext(); +builder.Services.AddScoped(); // Registrer repository +builder.Services.AddDbContext(options => +{ + var connectionString = builder.Configuration.GetConnectionString("DefaultConnectionString"); + options.UseNpgsql(connectionString); +}); + var app = builder.Build(); +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Database.Migrate(); // Apply any migrations + dbContext.SeedData(); // Run the seeder +} + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -17,4 +33,5 @@ } app.UseHttpsRedirection(); +MovieEndpoint.ConfigureCinemaEndpoint(app); 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..64b06f36 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,27 @@ +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.Repository +{ + public interface IRepository + { + Task> GetCustomers(); + Task AddCustomer(string name, string email, string phone); + Task UpdateCustomer(int id, string name, string email, string phone); + Task DeleteCustomer(int id); + + Task> GetMovies(); + Task AddMovie(string title, string rating, string descripition, int runtimemins); + Task UpdateMovie(int id, string title, string rating, string descripition, int runtimemins); + Task DeleteMovie(int id); + + Task> GetScreenings(int id); + + Task CreateScreening(int id,int screenNumber,int capacity,DateTime date); + + + + + + + } +} 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..e306520e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,152 @@ +using System.Net.Quic; +using System.Numerics; +using System.Xml.Linq; +using api_cinema_challenge.Data; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Repository +{ + public class Repository : IRepository + { + CinemaContext cc; + + public Repository(CinemaContext cc) + { + this.cc = cc; + } + + public async Task AddCustomer(string name, string email, string phone) + { + Customer customer = new Customer + { + Name = name, + Email = email, + Phone = phone, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + await cc.Customers.AddAsync(customer); + await cc.SaveChangesAsync(); + return customer; + } + + public async Task AddMovie(string title, string rating, string description, int runtimemins) + { + Movie movie = new Movie + { + Title = title, + Rating = rating, + Description = description, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + await cc.Movies.AddAsync(movie); + await cc.SaveChangesAsync(); + return movie; + } + + public async Task CreateScreening(int id, int screenNumber, int capacity, DateTime dateTime) + { + var movie = await cc.Movies.FirstOrDefaultAsync(x => x.Id == id); + if (movie == null) + { + return null; + } + + var screening = new Screening + { + ScreenNumber = screenNumber, + Capacity = capacity, + Movie = movie, + MovieId = movie.Id, + StartsAt = dateTime.ToUniversalTime(), // Konverter input til UTC + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + await cc.Screenings.AddAsync(screening); + await cc.SaveChangesAsync(); + return screening; + } + + public async Task DeleteCustomer(int id) + { + var customer = await cc.Customers.FirstOrDefaultAsync(x => x.Id == id); + if (customer == null) + { + return null; + } + + cc.Customers.Remove(customer); + await cc.SaveChangesAsync(); + return customer; + } + + public async Task DeleteMovie(int id) + { + var movie = await cc.Movies.FirstOrDefaultAsync(x => x.Id == id); + if (movie == null) + { + return null; + } + + cc.Movies.Remove(movie); + await cc.SaveChangesAsync(); + return movie; + } + + public async Task> GetCustomers() + { + return await cc.Customers.ToListAsync(); + } + + public async Task> GetMovies() + { + return await cc.Movies.ToListAsync(); + } + + public async Task> GetScreenings(int id) + { + return await cc.Screenings.Include(x => x.Movie).Where(x => x.MovieId == id).ToListAsync(); + } + + public async Task UpdateCustomer(int id, string name, string email, string phone) + { + var customer = await cc.Customers.FirstOrDefaultAsync(x => x.Id == id); + + if (customer == null) + { + return null; + } + + customer.Name = name; + customer.Email = email; + customer.Phone = phone; + customer.UpdatedAt = DateTime.UtcNow; + + await cc.SaveChangesAsync(); + return customer; + } + + public async Task UpdateMovie(int id, string title, string rating, string description, int runtimemins) + { + var movie = await cc.Movies.FirstOrDefaultAsync(x => x.Id == id); + + if (movie == null) + { + return null; + } + + movie.Title = title; + movie.Rating = rating; + movie.Description = description; + movie.UpdatedAt = DateTime.UtcNow; + + await cc.SaveChangesAsync(); + return movie; + } + } +} 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..d23e59f8 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -27,8 +27,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