From 382e4ae54e0bd8142e37d69cd3f3a2d7645ede9a Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Wed, 29 Jan 2025 17:28:10 +0100 Subject: [PATCH 1/6] Initial set up --- .../api-cinema-challenge/DTO/CustomerDTO.cs | 35 +++ .../api-cinema-challenge/DTO/MovieDTO.cs | 29 +++ .../api-cinema-challenge/DTO/ScreenDTO.cs | 33 +++ .../api-cinema-challenge/DTO/TicketDTO.cs | 27 +++ .../Data/CinemaContext.cs | 100 +++++++- .../Endpoints/CinemaEndpoint.cs | 27 +++ .../20250129150313_InitialCreate.Designer.cs | 222 ++++++++++++++++++ .../20250129150313_InitialCreate.cs | 145 ++++++++++++ .../Migrations/CinemaContextModelSnapshot.cs | 219 +++++++++++++++++ .../api-cinema-challenge/Models/Customer.cs | 29 +++ .../api-cinema-challenge/Models/Movie.cs | 28 +++ .../Models/MovieOnScreen.cs | 20 ++ .../api-cinema-challenge/Models/Screen.cs | 27 +++ .../api-cinema-challenge/Models/Ticket.cs | 22 ++ .../Repository/IRepository.cs | 7 + .../Repository/Repository.cs | 27 +++ .../api-cinema-challenge.csproj | 4 - 17 files changed, 996 insertions(+), 5 deletions(-) create mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Models/Customer.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Models/Movie.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Models/Screen.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs create mode 100644 api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs 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..2964ab8e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs @@ -0,0 +1,35 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class CustomerDTO + { + [Column("customerId")] + public int customerId { get; set; } + [Column("Name")] + public string Name { get; set; } + [Column("Email")] + public string Email { get; set; } + [Column("Phone")] + public string Phone { get; set; } + [Column("CreatedAt")] + public string CreatedAt { get; set; } + [Column("UpdatedAt")] + public string UpdatedAt { get; set; } + [Column("tickets")] + public virtual List tickets { get; set; } = new List(); + + public CustomerDTO(Customer customer) + { + customerId = customer.customerId; + Name = customer.Name; + Email = customer.Email; + Phone = customer.Phone; + CreatedAt = customer.CreatedAt.ToString(); + UpdatedAt = customer.UpdatedAt.ToString(); + //making ticket dtos + customer.tickets.ForEach(x => tickets.Add($" screenNumber: {x.screen.screenNumber} ")); + } + } +} 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..b547328f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs @@ -0,0 +1,29 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class MovieDTO + { + public int movieId { get; set; } + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public string RuntimeMins { get; set; } + public string CreatedAt { get; set; } + public string UpdatedAt { get; set; } + public virtual List screens { get; set; } = new List(); + + public MovieDTO(Movie movie) + { + movieId = movie.movieId; + Title = movie.Title; + Rating = movie.Rating; + Description = movie.Description; + RuntimeMins = movie.RuntimeMins; + CreatedAt = movie.CreatedAt.ToString(); + UpdatedAt = movie.UpdatedAt.ToString(); + movie.moviesOnScreens.ForEach(x => screens.Add(x.screen.screenNumber.ToString())); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs new file mode 100644 index 00000000..de85a2de --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs @@ -0,0 +1,33 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class ScreenDTO + { + public int screenId { 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 virtual List tickets { get; set; } + public virtual List movies { get; set; } + + public ScreenDTO(Screen screen) + { + screenId = screen.screenId; + screenNumber = screen.screenNumber; + capacity = screen.capacity; + startsAt = screen.startsAt; + createdAt = screen.createdAt; + updatedAt = screen.updatedAt; + tickets = new List(); + movies = new List(); + //convert tickets and movies to strings + screen.tickets.ForEach(x => tickets.Add($" ticket id : {x.ticketId}, customer: {x.customer.Name}")); + screen.moviesOnScreen.ForEach(x => movies.Add($" movie id {x.movieId}, movie title {x.movie.Title}")); + + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs new file mode 100644 index 00000000..6c116073 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs @@ -0,0 +1,27 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class TicketDTO + { + + public int ticketId { get; set; } + + public Screen screen { get; set; } + + public int customerID { get; set; } + + public virtual string customer { get; set; } + + public TicketDTO(Ticket ticket) + { + ticketId = ticket.ticketId; + screen = ticket.screen; + customerID = ticket.customerID; + //fill in customer + throw new NotImplementedException(); + } + } + +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index ad4fe854..a411adc5 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -1,4 +1,7 @@ -using Microsoft.EntityFrameworkCore; +using api_cinema_challenge.DTO; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.JSInterop.Infrastructure; using Newtonsoft.Json.Linq; namespace api_cinema_challenge.Data @@ -10,17 +13,112 @@ public CinemaContext(DbContextOptions options) : base(options) { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; + this.Database.SetConnectionString(_connectionString); this.Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(_connectionString); + //optionsBuilder.UseLazyLoadingProxies(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { + //keys + modelBuilder.Entity() + .HasKey(a => a.customerId); + + modelBuilder.Entity() + .HasKey(a => a.movieId); + + modelBuilder.Entity() + .HasKey(a => a.screenId); + + modelBuilder.Entity() + .HasKey(a => a.ticketId); + modelBuilder.Entity() + .HasKey(a => new {a.movieId, a.screenId}); + + //defining relations + modelBuilder.Entity() + .HasMany(a => a.tickets) + .WithOne(a => a.customer); + + modelBuilder.Entity() + .HasOne(a => a.customer) + .WithMany(a => a.tickets) + .HasForeignKey(a => a.customerID); + modelBuilder.Entity() + .HasOne(a => a.screen) + .WithMany(a => a.tickets); + + + modelBuilder.Entity() + .HasMany(a => a.tickets) + .WithOne(a => a.screen) + .HasForeignKey(a => a.ticketId); + + modelBuilder.Entity() + .HasMany(a => a.moviesOnScreen) + .WithOne(a => a.screen); + + modelBuilder.Entity() + .HasOne(a => a.screen) + .WithMany(a => a.moviesOnScreen) + .HasForeignKey(a => a.screenId); + + modelBuilder.Entity() + .HasOne(a => a.movie) + .WithMany(a => a.moviesOnScreens) + .HasForeignKey(a => a.movieId); + + modelBuilder.Entity() + .HasMany(a => a.moviesOnScreens) + .WithOne(a => a.movie); + + //seeding + modelBuilder.Entity() + .HasData( + new List + { + new Movie { movieId=1 ,Title = "the hobbit", CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc) ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Customer {customerId =1, Name="bob" , CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), Email="bob@gmail.com", Phone="12345678" , UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Screen {screenId=1, capacity=2, createdAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), screenNumber=1, startsAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), updatedAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Ticket {ticketId=1, customerID=1} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new MovieOnScreen { movieId=1, screenId=1} + } + ); } + public DbSet Movies { get; set; } + public DbSet Customers { get; set; } + public DbSet Screens { get; set; } + public DbSet Tickets { get; set; } + public DbSet movieOnScreens { get; set; } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs new file mode 100644 index 00000000..d422c93e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -0,0 +1,27 @@ +using api_cinema_challenge.DTO; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using Microsoft.AspNetCore.Mvc; + +namespace api_cinema_challenge.Endpoints +{ + public static class CinemaEndpoint + { + public static void ConfigureCinema(this WebApplication app) + { + var cinema = app.MapGroup("/cinema"); + + cinema.MapGet("/", GetAllMovies); + + + } + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetAllMovies(IRepository movieRepo) + { + var results = await movieRepo.GetAll(); + List movieDTOs = new List(); + results.ToList().ForEach(x => movieDTOs.Add(new MovieDTO(x))); + return TypedResults.Ok(movieDTOs); + } + } +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs new file mode 100644 index 00000000..9c85e509 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs @@ -0,0 +1,222 @@ +// +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("20250129150313_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MovieScreen", b => + { + b.Property("moviesmovieId") + .HasColumnType("integer"); + + b.Property("screensscreenId") + .HasColumnType("integer"); + + b.HasKey("moviesmovieId", "screensscreenId"); + + b.HasIndex("screensscreenId"); + + b.ToTable("MovieScreen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("customerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("customerId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("customerId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + 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("UpdatedAt"); + + b.HasKey("customerId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("movieId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("movieId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("movieId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Rating"); + + b.Property("RuntimeMins") + .IsRequired() + .HasColumnType("text") + .HasColumnName("RuntimeMins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("movieId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Property("screenId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("screenId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("screenId")); + + b.Property("capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("createdAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdAt"); + + b.Property("screenNumber") + .HasColumnType("integer") + .HasColumnName("screenNumber"); + + b.Property("startsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("startsAt"); + + b.Property("updatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("screenId"); + + b.ToTable("Screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("ticketId") + .HasColumnType("integer") + .HasColumnName("ticketId"); + + b.Property("customerID") + .HasColumnType("integer") + .HasColumnName("customerId"); + + b.HasKey("ticketId"); + + b.HasIndex("customerID"); + + b.ToTable("Ticket"); + }); + + modelBuilder.Entity("MovieScreen", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", null) + .WithMany() + .HasForeignKey("moviesmovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", null) + .WithMany() + .HasForeignKey("screensscreenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Screen", "screen") + .WithMany("tickets") + .HasForeignKey("ticketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("customer"); + + b.Navigation("screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Navigation("tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs new file mode 100644 index 00000000..46cc8ca4 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs @@ -0,0 +1,145 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Customer", + columns: table => new + { + customerId = 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_Customer", x => x.customerId); + }); + + migrationBuilder.CreateTable( + name: "Movie", + columns: table => new + { + movieId = 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: "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_Movie", x => x.movieId); + }); + + migrationBuilder.CreateTable( + name: "Screen", + columns: table => new + { + screenId = 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) + }, + constraints: table => + { + table.PrimaryKey("PK_Screen", x => x.screenId); + }); + + migrationBuilder.CreateTable( + name: "MovieScreen", + columns: table => new + { + moviesmovieId = table.Column(type: "integer", nullable: false), + screensscreenId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MovieScreen", x => new { x.moviesmovieId, x.screensscreenId }); + table.ForeignKey( + name: "FK_MovieScreen_Movie_moviesmovieId", + column: x => x.moviesmovieId, + principalTable: "Movie", + principalColumn: "movieId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MovieScreen_Screen_screensscreenId", + column: x => x.screensscreenId, + principalTable: "Screen", + principalColumn: "screenId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Ticket", + columns: table => new + { + ticketId = table.Column(type: "integer", nullable: false), + customerId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Ticket", x => x.ticketId); + table.ForeignKey( + name: "FK_Ticket_Customer_customerId", + column: x => x.customerId, + principalTable: "Customer", + principalColumn: "customerId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Ticket_Screen_ticketId", + column: x => x.ticketId, + principalTable: "Screen", + principalColumn: "screenId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_MovieScreen_screensscreenId", + table: "MovieScreen", + column: "screensscreenId"); + + migrationBuilder.CreateIndex( + name: "IX_Ticket_customerId", + table: "Ticket", + column: "customerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MovieScreen"); + + migrationBuilder.DropTable( + name: "Ticket"); + + migrationBuilder.DropTable( + name: "Movie"); + + migrationBuilder.DropTable( + name: "Customer"); + + migrationBuilder.DropTable( + name: "Screen"); + } + } +} 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..28ef1002 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,219 @@ +// +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("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MovieScreen", b => + { + b.Property("moviesmovieId") + .HasColumnType("integer"); + + b.Property("screensscreenId") + .HasColumnType("integer"); + + b.HasKey("moviesmovieId", "screensscreenId"); + + b.HasIndex("screensscreenId"); + + b.ToTable("MovieScreen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("customerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("customerId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("customerId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + 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("UpdatedAt"); + + b.HasKey("customerId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("movieId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("movieId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("movieId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Rating"); + + b.Property("RuntimeMins") + .IsRequired() + .HasColumnType("text") + .HasColumnName("RuntimeMins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("movieId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Property("screenId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("screenId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("screenId")); + + b.Property("capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("createdAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdAt"); + + b.Property("screenNumber") + .HasColumnType("integer") + .HasColumnName("screenNumber"); + + b.Property("startsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("startsAt"); + + b.Property("updatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("screenId"); + + b.ToTable("Screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("ticketId") + .HasColumnType("integer") + .HasColumnName("ticketId"); + + b.Property("customerID") + .HasColumnType("integer") + .HasColumnName("customerId"); + + b.HasKey("ticketId"); + + b.HasIndex("customerID"); + + b.ToTable("Ticket"); + }); + + modelBuilder.Entity("MovieScreen", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", null) + .WithMany() + .HasForeignKey("moviesmovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", null) + .WithMany() + .HasForeignKey("screensscreenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + 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.Screen", "screen") + .WithMany("tickets") + .HasForeignKey("ticketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("customer"); + + b.Navigation("screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Navigation("tickets"); + }); +#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..f6e4e152 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + [Table("Customer")] + [PrimaryKey("customerId")] + public class Customer + { + [Column("customerId")] + public int customerId { get; set; } + [Column("Name")] + public string Name { get; set; } + [Column("Email")] + public string Email { get; set; } + [Column("Phone")] + public string Phone { get; set; } + [Column("CreatedAt")] + public DateTime CreatedAt { get; set; } + [Column("UpdatedAt")] + public DateTime UpdatedAt { get; set; } + [Column("tickets")] + public List tickets { get; set; } = new List(); + [NotMapped] + public int ticketId { 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..e7287150 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,28 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + [Table("Movie")] + [PrimaryKey("movieId")] + public class Movie + { + [Column("movieId")] + public int movieId { get; set; } + [Column("Title")] + public string Title { get; set; } + [Column("Rating")] + public string Rating { get; set; } + [Column("Description")] + public string Description { get; set; } + [Column("RuntimeMins")] + public string RuntimeMins { get; set; } + [Column("CreatedAt")] + public DateTime CreatedAt { get; set; } + [Column("UpdatedAt")] + public DateTime UpdatedAt { get; set; } + [Column("moviesOnScreens")] + public List moviesOnScreens { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs b/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs new file mode 100644 index 00000000..91b6c5a6 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + [Table("movieOnScreen")] + [PrimaryKey("movieId","screenId" )] + public class MovieOnScreen + { + [Column("movie")] + public Movie movie { get; set; } + [Column("screen")] + public Screen screen { get; set; } + [Column("movieId")] + public int movieId { get; set; } + [Column("screenId")] + public int screenId { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs new file mode 100644 index 00000000..26e4f58f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + [Table("Screen")] + [PrimaryKey("screenId")] + public class Screen + { + [Column("screenId")] + public int screenId { get; set; } + [Column("screenNumber")] + public int screenNumber { get; set; } + [Column("capacity")] + public int capacity { get; set; } + [Column("startsAt")] + public DateTime startsAt { get; set; } + [Column("createdAt")] + public DateTime createdAt { get; set; } + [Column("UpdatedAt")] + public DateTime updatedAt { get; set; } + [Column("tickets")] + public List tickets { get; set; } + [Column("movies")] + public List moviesOnScreen { 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..192bd269 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + //你看不懂这个吧 + [Table("Ticket")] + [PrimaryKey("ticketId")] + public class Ticket + { + [Column("ticketId")] + public int ticketId { get; set; } + [Column("screen")] + public Screen screen { get; set; } + [Column("screenId")] + public int screenId { get; set; } + [Column("customerId")] + public int customerID { get; set; } + [Column("customer")] + public Customer customer { get; set; } + } +} 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..f4326d2a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,7 @@ +namespace api_cinema_challenge.Repository +{ + public interface IRepository + { + public Task> GetAll(); + } +} 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..97061cf2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,27 @@ +using api_cinema_challenge.Data; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.Internal; + +namespace api_cinema_challenge.Repository +{ + public class Repository : IRepository where T : class + { + private CinemaContext _db; + private DbSet _table = null!; + + public Repository(CinemaContext db) + { + _db = db; + _table = _db.Set(); + } + + public async Task> GetAll() + { + List movies = _table.ToList(); + return movies; + } + + + } +} 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 @@ - - - - From 63dd31750b3f811a2a87cd493533ae52d2949532 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Wed, 29 Jan 2025 18:17:29 +0100 Subject: [PATCH 2/6] implemented getall methods --- .../api-cinema-challenge/Data/CinemaContext.cs | 5 +++-- .../Endpoints/CinemaEndpoint.cs | 16 +++++++++++++++- .../api-cinema-challenge/Models/Customer.cs | 4 ++-- .../api-cinema-challenge/Models/Movie.cs | 4 ++-- .../api-cinema-challenge/Models/MovieOnScreen.cs | 8 ++++---- .../api-cinema-challenge/Models/Screen.cs | 6 +++--- .../api-cinema-challenge/Models/Ticket.cs | 8 ++++---- .../api-cinema-challenge/Program.cs | 5 +++++ .../Repository/IRepository.cs | 2 ++ .../Repository/Repository.cs | 2 +- .../api-cinema-challenge.csproj | 1 + 11 files changed, 42 insertions(+), 19 deletions(-) diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index a411adc5..8e4e6e70 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -4,6 +4,7 @@ using Microsoft.JSInterop.Infrastructure; using Newtonsoft.Json.Linq; + namespace api_cinema_challenge.Data { public class CinemaContext : DbContext @@ -20,7 +21,7 @@ public CinemaContext(DbContextOptions options) : base(options) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(_connectionString); - //optionsBuilder.UseLazyLoadingProxies(); + optionsBuilder.UseLazyLoadingProxies(); } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -83,7 +84,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasData( new List { - new Movie { movieId=1 ,Title = "the hobbit", CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc) ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} + new Movie { movieId=1 ,Title = "the hobbit", CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc) ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} } ); modelBuilder.Entity() diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs index d422c93e..1c706ce0 100644 --- a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -11,10 +11,12 @@ public static void ConfigureCinema(this WebApplication app) { var cinema = app.MapGroup("/cinema"); - cinema.MapGet("/", GetAllMovies); + cinema.MapGet("/movies", GetAllMovies); + cinema.MapGet("/customers", GetAllMovies); } + #region Movies [ProducesResponseType(StatusCodes.Status200OK)] public static async Task GetAllMovies(IRepository movieRepo) { @@ -23,5 +25,17 @@ public static async Task GetAllMovies(IRepository movieRepo) results.ToList().ForEach(x => movieDTOs.Add(new MovieDTO(x))); return TypedResults.Ok(movieDTOs); } + #endregion + + #region Customers + public static async Task GetAllCustomers(IRepository customerRepo) + { + var results = await customerRepo.GetAll(); + List customerDTOs = new List(); + results.ToList().ForEach(x => customerDTOs.Add(new CustomerDTO(x))); + return TypedResults.Ok(customerDTOs); + } + #endregion + } } \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs index f6e4e152..9e25f062 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -19,8 +19,8 @@ public class Customer public DateTime CreatedAt { get; set; } [Column("UpdatedAt")] public DateTime UpdatedAt { get; set; } - [Column("tickets")] - public List tickets { get; set; } = new List(); + [NotMapped] + public virtual List tickets { get; set; } = new List(); [NotMapped] public int ticketId { get; set; } diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs index e7287150..e3870039 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -21,8 +21,8 @@ public class Movie public DateTime CreatedAt { get; set; } [Column("UpdatedAt")] public DateTime UpdatedAt { get; set; } - [Column("moviesOnScreens")] - public List moviesOnScreens { get; set; } + [NotMapped] + public virtual List moviesOnScreens { get; set; } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs b/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs index 91b6c5a6..2dd07c39 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs @@ -7,10 +7,10 @@ namespace api_cinema_challenge.Models [PrimaryKey("movieId","screenId" )] public class MovieOnScreen { - [Column("movie")] - public Movie movie { get; set; } - [Column("screen")] - public Screen screen { get; set; } + [NotMapped] + public virtual Movie movie { get; set; } + [NotMapped] + public virtual Screen screen { get; set; } [Column("movieId")] public int movieId { get; set; } [Column("screenId")] diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs index 26e4f58f..eb5d8857 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs @@ -20,8 +20,8 @@ public class Screen [Column("UpdatedAt")] public DateTime updatedAt { get; set; } [Column("tickets")] - public List tickets { get; set; } - [Column("movies")] - public List moviesOnScreen { get; set; } + public virtual List tickets { get; set; } + [NotMapped] + public virtual List moviesOnScreen { get; set; } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs index 192bd269..4ddf3618 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -10,13 +10,13 @@ public class Ticket { [Column("ticketId")] public int ticketId { get; set; } - [Column("screen")] - public Screen screen { get; set; } + [NotMapped] + public virtual Screen screen { get; set; } [Column("screenId")] public int screenId { get; set; } [Column("customerId")] public int customerID { get; set; } - [Column("customer")] - public Customer customer { get; set; } + [NotMapped] + public virtual 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..0616b7c6 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,4 +1,7 @@ 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); @@ -6,6 +9,7 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(); +builder.Services.AddScoped, Repository>(); var app = builder.Build(); @@ -17,4 +21,5 @@ } app.UseHttpsRedirection(); +app.ConfigureCinema(); app.Run(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs index f4326d2a..e5d76911 100644 --- a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -3,5 +3,7 @@ public interface IRepository { public Task> GetAll(); + //public Task Get(int id); + } } diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs index 97061cf2..43ddb8a7 100644 --- a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -21,7 +21,7 @@ public async Task> GetAll() List movies = _table.ToList(); return movies; } - + } } 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 d23e59f8..2a425aa2 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -17,6 +17,7 @@ + all From 3c23eb727245b9753f797f65c2ac693be6926e7f Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Wed, 29 Jan 2025 21:38:22 +0100 Subject: [PATCH 3/6] implemented get alls --- .../DTO/MovieOnScreenDTO.cs | 22 ++++++++++++ .../api-cinema-challenge/DTO/TicketDTO.cs | 8 ++--- .../Endpoints/CinemaEndpoint.cs | 34 +++++++++++++++++-- .../api-cinema-challenge/Program.cs | 4 +++ .../Repository/Repository.cs | 2 -- 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs new file mode 100644 index 00000000..49847b64 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs @@ -0,0 +1,22 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class MovieOnScreenDTO + { + + public virtual string movieTitle { get; set; } + public virtual string screenNumber { get; set; } + public int movieId { get; set; } + public int screenId { get; set; } + + public MovieOnScreenDTO(MovieOnScreen movieOnScreen) + { + movieTitle = movieOnScreen.movie.Title; + screenNumber = movieOnScreen.screen.screenNumber.ToString(); + movieId = movieOnScreen.movieId; + screenId = movieOnScreen.screenId; + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs index 6c116073..bef9fb3f 100644 --- a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs +++ b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs @@ -8,19 +8,19 @@ public class TicketDTO public int ticketId { get; set; } - public Screen screen { get; set; } + public string screenNumber { get; set; } public int customerID { get; set; } - public virtual string customer { get; set; } + public string customer { get; set; } public TicketDTO(Ticket ticket) { ticketId = ticket.ticketId; - screen = ticket.screen; + screenNumber = ticket.screen.screenNumber.ToString(); customerID = ticket.customerID; //fill in customer - throw new NotImplementedException(); + customer = ticket.customer.Name.ToString(); } } diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs index 1c706ce0..ad515d70 100644 --- a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -1,4 +1,5 @@ -using api_cinema_challenge.DTO; +using System.Reflection.Emit; +using api_cinema_challenge.DTO; using api_cinema_challenge.Models; using api_cinema_challenge.Repository; using Microsoft.AspNetCore.Mvc; @@ -13,7 +14,8 @@ public static void ConfigureCinema(this WebApplication app) cinema.MapGet("/movies", GetAllMovies); cinema.MapGet("/customers", GetAllMovies); - + cinema.MapGet("/tickets", GetAllTickets); + cinema.MapGet("/screens", GetAllScreens); } #region Movies @@ -36,6 +38,32 @@ public static async Task GetAllCustomers(IRepository customer return TypedResults.Ok(customerDTOs); } #endregion - + #region Tickets + public static async Task GetAllTickets(IRepository ticketRepo) + { + var results = await ticketRepo.GetAll(); + List ticketDTOs = new List(); + results.ToList().ForEach(x => ticketDTOs.Add(new TicketDTO(x))); + return TypedResults.Ok(ticketDTOs); + } + #endregion + #region Screens + public static async Task GetAllScreens(IRepository screenRepo) + { + var results = await screenRepo.GetAll(); + List screenDTOs = new List(); + results.ToList().ForEach(x => screenDTOs.Add(new ScreenDTO(x))); + return TypedResults.Ok(screenDTOs); + } + #endregion + #region MovieScreenings + public static async Task GetAllScreenings(IRepository screeningRepo) + { + var results = await screeningRepo.GetAll(); + List screeningDTOs = new List(); + results.ToList().ForEach(x => screeningDTOs.Add(new MovieOnScreenDTO(x))); + return TypedResults.Ok(screeningDTOs); + } + #endregion } } \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index 0616b7c6..b31dade5 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -10,6 +10,10 @@ builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(); builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); var app = builder.Build(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs index 43ddb8a7..22724275 100644 --- a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -21,7 +21,5 @@ public async Task> GetAll() List movies = _table.ToList(); return movies; } - - } } From 43cf6f569484cc455d9dc099e0bb40d084a04819 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Sat, 1 Feb 2025 23:49:42 +0100 Subject: [PATCH 4/6] extension and core --- .../api-cinema-challenge/DTO/MovieDTO.cs | 2 +- .../DTO/MovieOnScreenDTO.cs | 22 -- .../api-cinema-challenge/DTO/ScreenDTO.cs | 12 +- .../api-cinema-challenge/DTO/TicketDTO.cs | 7 +- .../Data/CinemaContext.cs | 41 ++-- .../Endpoints/CinemaEndpoint.cs | 198 +++++++++++++++++- .../api-cinema-challenge/Models/Customer.cs | 4 +- .../api-cinema-challenge/Models/Movie.cs | 7 +- .../Models/MovieOnScreen.cs | 20 -- .../api-cinema-challenge/Models/Screen.cs | 14 +- .../api-cinema-challenge/Models/Ticket.cs | 2 + .../api-cinema-challenge/Program.cs | 2 +- .../Repository/IRepository.cs | 22 +- .../Repository/Repository.cs | 53 ++++- 14 files changed, 291 insertions(+), 115 deletions(-) delete mode 100644 api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs delete mode 100644 api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs index b547328f..f31c3205 100644 --- a/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs +++ b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs @@ -23,7 +23,7 @@ public MovieDTO(Movie movie) RuntimeMins = movie.RuntimeMins; CreatedAt = movie.CreatedAt.ToString(); UpdatedAt = movie.UpdatedAt.ToString(); - movie.moviesOnScreens.ForEach(x => screens.Add(x.screen.screenNumber.ToString())); + movie.screens.ForEach(x => screens.Add(x.screenNumber.ToString())); } } } diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs deleted file mode 100644 index 49847b64..00000000 --- a/api-cinema-challenge/api-cinema-challenge/DTO/MovieOnScreenDTO.cs +++ /dev/null @@ -1,22 +0,0 @@ -using api_cinema_challenge.Models; -using System.ComponentModel.DataAnnotations.Schema; - -namespace api_cinema_challenge.DTO -{ - public class MovieOnScreenDTO - { - - public virtual string movieTitle { get; set; } - public virtual string screenNumber { get; set; } - public int movieId { get; set; } - public int screenId { get; set; } - - public MovieOnScreenDTO(MovieOnScreen movieOnScreen) - { - movieTitle = movieOnScreen.movie.Title; - screenNumber = movieOnScreen.screen.screenNumber.ToString(); - movieId = movieOnScreen.movieId; - screenId = movieOnScreen.screenId; - } - } -} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs index de85a2de..d4a57d4f 100644 --- a/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs +++ b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs @@ -8,11 +8,11 @@ public class ScreenDTO public int screenId { 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 string startsAt { get; set; } + public string createdAt { get; set; } + public string updatedAt { get; set; } public virtual List tickets { get; set; } - public virtual List movies { get; set; } + public virtual string movie { get; set; } public ScreenDTO(Screen screen) { @@ -23,10 +23,10 @@ public ScreenDTO(Screen screen) createdAt = screen.createdAt; updatedAt = screen.updatedAt; tickets = new List(); - movies = new List(); + //convert tickets and movies to strings screen.tickets.ForEach(x => tickets.Add($" ticket id : {x.ticketId}, customer: {x.customer.Name}")); - screen.moviesOnScreen.ForEach(x => movies.Add($" movie id {x.movieId}, movie title {x.movie.Title}")); + movie = $" movie id {screen.movieId}, movie title {screen.movie.Title}"; } } diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs index bef9fb3f..3b20103f 100644 --- a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs +++ b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs @@ -8,16 +8,19 @@ public class TicketDTO public int ticketId { get; set; } - public string screenNumber { get; set; } + public int screenNumber { get; set; } public int customerID { get; set; } public string customer { get; set; } + + + public TicketDTO(Ticket ticket) { ticketId = ticket.ticketId; - screenNumber = ticket.screen.screenNumber.ToString(); + screenNumber = ticket.screen.screenNumber; customerID = ticket.customerID; //fill in customer customer = ticket.customer.Name.ToString(); diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index 8e4e6e70..0135298e 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -39,8 +39,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasKey(a => a.ticketId); - modelBuilder.Entity() - .HasKey(a => new {a.movieId, a.screenId}); + //defining relations modelBuilder.Entity() @@ -51,32 +50,22 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasOne(a => a.customer) .WithMany(a => a.tickets) .HasForeignKey(a => a.customerID); + modelBuilder.Entity() .HasOne(a => a.screen) .WithMany(a => a.tickets); - modelBuilder.Entity() - .HasMany(a => a.tickets) - .WithOne(a => a.screen) - .HasForeignKey(a => a.ticketId); + .HasOne(a => a.movie) + .WithMany(a => a.screens); modelBuilder.Entity() - .HasMany(a => a.moviesOnScreen) + .HasMany(a => a.tickets) .WithOne(a => a.screen); - modelBuilder.Entity() - .HasOne(a => a.screen) - .WithMany(a => a.moviesOnScreen) - .HasForeignKey(a => a.screenId); - - modelBuilder.Entity() - .HasOne(a => a.movie) - .WithMany(a => a.moviesOnScreens) - .HasForeignKey(a => a.movieId); modelBuilder.Entity() - .HasMany(a => a.moviesOnScreens) + .HasMany(a => a.screens) .WithOne(a => a.movie); //seeding @@ -84,42 +73,36 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasData( new List { - new Movie { movieId=1 ,Title = "the hobbit", CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc) ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} + new Movie { movieId=1 ,Title = "the hobbit",UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} } ); modelBuilder.Entity() .HasData( new List { - new Customer {customerId =1, Name="bob" , CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), Email="bob@gmail.com", Phone="12345678" , UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)} + new Customer {customerId =1, Name="bob" , CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Email="bob@gmail.com", Phone="12345678" , UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString()} } ); modelBuilder.Entity() .HasData( new List { - new Screen {screenId=1, capacity=2, createdAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), screenNumber=1, startsAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc), updatedAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)} + new Screen {screenId=1, capacity=2, movieId=1,createdAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), screenNumber=1, startsAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), updatedAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString()} } ); modelBuilder.Entity() .HasData( new List { - new Ticket {ticketId=1, customerID=1} - } - ); - modelBuilder.Entity() - .HasData( - new List - { - new MovieOnScreen { movieId=1, screenId=1} + new Ticket {ticketId=1, customerID=1, screenId=1} } ); + } public DbSet Movies { get; set; } public DbSet Customers { get; set; } public DbSet Screens { get; set; } public DbSet Tickets { get; set; } - public DbSet movieOnScreens { get; set; } + } } diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs index ad515d70..fcf48351 100644 --- a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -1,7 +1,10 @@ -using System.Reflection.Emit; +using System.ComponentModel.DataAnnotations.Schema; +using System.Globalization; +using System.Reflection.Emit; using api_cinema_challenge.DTO; using api_cinema_challenge.Models; using api_cinema_challenge.Repository; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; namespace api_cinema_challenge.Endpoints @@ -13,57 +16,230 @@ public static void ConfigureCinema(this WebApplication app) var cinema = app.MapGroup("/cinema"); cinema.MapGet("/movies", GetAllMovies); - cinema.MapGet("/customers", GetAllMovies); + cinema.MapPost("/newmovie", CreateMovie); + cinema.MapPut("/updatemovie", UpdateMovie); + cinema.MapDelete("/deletemovie", DeleteMovie); + + cinema.MapGet("/customers", GetAllCustomers); + cinema.MapPost("/newcustomer", CreateCustomer); + cinema.MapPut("/updatecustomer", UpdateCustomer); + cinema.MapDelete("/deletecustomer", DeleteCustomer); + cinema.MapGet("/tickets", GetAllTickets); + cinema.MapPost("/newticket", CreateTicket); + + cinema.MapGet("/screens", GetAllScreens); + cinema.MapPost("/newscreen", CreateScreen); + } #region Movies [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] public static async Task GetAllMovies(IRepository movieRepo) { - var results = await movieRepo.GetAll(); + var results = movieRepo.GetAll(); List movieDTOs = new List(); results.ToList().ForEach(x => movieDTOs.Add(new MovieDTO(x))); return TypedResults.Ok(movieDTOs); } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateMovie(IRepository repo, string title, string runtime, string description, string rating) + { + + try + { + Movie movie = new Movie { CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Description = description, Title = title, RuntimeMins = runtime, Rating = rating, UpdatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; + repo.Insert(movie); + repo.Save(); + return TypedResults.Ok(movie); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateMovie(IRepository repo, int id, string title, string runtime, string description, string rating) + { + try + { + Movie movie = repo.GetById(id); + movie.Title = title; + movie.Description = description; + movie.RuntimeMins = runtime; + movie.Rating = rating; + movie.UpdatedAt = DateTime.Now.ToString(); + + repo.Save(); + return TypedResults.Ok(new MovieDTO(movie)); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task DeleteMovie(IRepository repo, int id) + { + try + { + repo.Delete(id); + + repo.Save(); + return TypedResults.Ok(); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + + #endregion #region Customers + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] public static async Task GetAllCustomers(IRepository customerRepo) { - var results = await customerRepo.GetAll(); + var results = customerRepo.GetAll(); List customerDTOs = new List(); results.ToList().ForEach(x => customerDTOs.Add(new CustomerDTO(x))); return TypedResults.Ok(customerDTOs); } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateCustomer(IRepository repo, string name, string email, string phone) + { + + try + { + Customer customer = new Customer { CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Name = name, Email = email, Phone = phone, UpdatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; + repo.Insert(customer); + repo.Save(); + return TypedResults.Ok(customer); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateCustomer(IRepository repo, int id, string name, string email, string phone) + { + try + { + Customer customer = repo.GetById(id); + customer.Name = name; + customer.Email = email; + customer.Phone = phone; + customer.UpdatedAt = DateTime.Now.ToString(); + + repo.Save(); + return TypedResults.Ok(new CustomerDTO(customer)); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task DeleteCustomer(IRepository repo, int id) + { + try + { + repo.Delete(id); + + repo.Save(); + return TypedResults.Ok(); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } #endregion #region Tickets + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] public static async Task GetAllTickets(IRepository ticketRepo) { - var results = await ticketRepo.GetAll(); + var results = ticketRepo.GetAll(); List ticketDTOs = new List(); results.ToList().ForEach(x => ticketDTOs.Add(new TicketDTO(x))); return TypedResults.Ok(ticketDTOs); } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateTicket(IRepository repo, int customerId, int screenId) + { + + + Ticket ticket = new Ticket { customerID = customerId, screenId = screenId }; + repo.Insert(ticket); + //repo.Save(); + return TypedResults.Ok(new TicketDTO(ticket)); + + + + } + + + + #endregion #region Screens + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] public static async Task GetAllScreens(IRepository screenRepo) { - var results = await screenRepo.GetAll(); + var results = screenRepo.GetAll(); List screenDTOs = new List(); results.ToList().ForEach(x => screenDTOs.Add(new ScreenDTO(x))); return TypedResults.Ok(screenDTOs); } #endregion #region MovieScreenings - public static async Task GetAllScreenings(IRepository screeningRepo) + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllScreenings(IRepository screeningRepo) { - var results = await screeningRepo.GetAll(); - List screeningDTOs = new List(); - results.ToList().ForEach(x => screeningDTOs.Add(new MovieOnScreenDTO(x))); + var results = screeningRepo.GetAll(); + List screeningDTOs = new List(); + results.ToList().ForEach(x => screeningDTOs.Add(new ScreenDTO(x))); return TypedResults.Ok(screeningDTOs); } - #endregion + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateScreen(IRepository repo, int capacity, int screenNumber, string startsAt, int movieId) + { + try + { + Screen screen = new Screen(); + screen.capacity = capacity; + screen.screenNumber = screenNumber; + screen.startsAt = startsAt; + screen.movieId = movieId; + repo.Insert(screen); + repo.Save(); + return TypedResults.Ok(new ScreenDTO(screen)); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + #endregion + } } } \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs index 9e25f062..004b8417 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -16,9 +16,9 @@ public class Customer [Column("Phone")] public string Phone { get; set; } [Column("CreatedAt")] - public DateTime CreatedAt { get; set; } + public string CreatedAt { get; set; } [Column("UpdatedAt")] - public DateTime UpdatedAt { get; set; } + public string UpdatedAt { get; set; } [NotMapped] public virtual List tickets { get; set; } = new List(); [NotMapped] diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs index e3870039..888b6181 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -18,11 +18,12 @@ public class Movie [Column("RuntimeMins")] public string RuntimeMins { get; set; } [Column("CreatedAt")] - public DateTime CreatedAt { get; set; } + public string CreatedAt { get; set; } [Column("UpdatedAt")] - public DateTime UpdatedAt { get; set; } + public string UpdatedAt { get; set; } [NotMapped] - public virtual List moviesOnScreens { get; set; } + public virtual List screens { get; set; } + } } diff --git a/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs b/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs deleted file mode 100644 index 2dd07c39..00000000 --- a/api-cinema-challenge/api-cinema-challenge/Models/MovieOnScreen.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore; - -namespace api_cinema_challenge.Models -{ - [Table("movieOnScreen")] - [PrimaryKey("movieId","screenId" )] - public class MovieOnScreen - { - [NotMapped] - public virtual Movie movie { get; set; } - [NotMapped] - public virtual Screen screen { get; set; } - [Column("movieId")] - public int movieId { get; set; } - [Column("screenId")] - public int screenId { get; set; } - - } -} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs index eb5d8857..05dc1e1d 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs @@ -14,14 +14,16 @@ public class Screen [Column("capacity")] public int capacity { get; set; } [Column("startsAt")] - public DateTime startsAt { get; set; } + public string startsAt { get; set; } [Column("createdAt")] - public DateTime createdAt { get; set; } + public string createdAt { get; set; } [Column("UpdatedAt")] - public DateTime updatedAt { get; set; } - [Column("tickets")] - public virtual List tickets { get; set; } + public string updatedAt { get; set; } + [Column("movieId")] + public int movieId{ get; set; } [NotMapped] - public virtual List moviesOnScreen { get; set; } + public virtual Movie movie{ get; set; } + [NotMapped] + public virtual List tickets{ get; set; } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs index 4ddf3618..8554a4d2 100644 --- a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; namespace api_cinema_challenge.Models @@ -17,6 +18,7 @@ public class Ticket [Column("customerId")] public int customerID { get; set; } [NotMapped] + public virtual 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 b31dade5..fefb1054 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -12,7 +12,7 @@ builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); builder.Services.AddScoped, Repository>(); -builder.Services.AddScoped, Repository>(); + builder.Services.AddScoped, Repository>(); var app = builder.Build(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs index e5d76911..f980176d 100644 --- a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -1,9 +1,19 @@ -namespace api_cinema_challenge.Repository +using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; + +namespace api_cinema_challenge.Repository { - public interface IRepository - { - public Task> GetAll(); - //public Task Get(int id); + public interface IRepository where T : class + { + public IEnumerable GetAll(); + IEnumerable GetAll(params Expression>[] includeExpressions); + T GetById(object id); + void Insert(T obj); + void Update(T obj); + void Delete(object id); + void Save(); + DbSet Table { get; } - } + } + } diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs index 22724275..9fc0cf84 100644 --- a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -1,14 +1,17 @@ -using api_cinema_challenge.Data; +using System.Linq.Expressions; +using api_cinema_challenge.Data; using api_cinema_challenge.Models; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query.Internal; namespace api_cinema_challenge.Repository { - public class Repository : IRepository where T : class + public class Repository : IRepository where T : class { + + private CinemaContext _db; - private DbSet _table = null!; + private DbSet _table = null; public Repository(CinemaContext db) { @@ -16,10 +19,48 @@ public Repository(CinemaContext db) _table = _db.Set(); } - public async Task> GetAll() + public IEnumerable GetAll(params Expression>[] includeExpressions) + { + if (includeExpressions.Any()) + { + var set = includeExpressions + .Aggregate>, IQueryable> + (_table, (current, expression) => current.Include(expression)); + } + return _table.ToList(); + } + + public IEnumerable GetAll() + { + return _table.ToList(); + } + public T GetById(object id) + { + return _table.Find(id); + } + + public void Insert(T obj) { - List movies = _table.ToList(); - return movies; + _table.Add(obj); } + public void Update(T obj) + { + _table.Attach(obj); + _db.Entry(obj).State = EntityState.Modified; + } + + public void Delete(object id) + { + T existing = _table.Find(id); + _table.Remove(existing); + } + + + public void Save() + { + _db.SaveChanges(); + } + public DbSet Table { get { return _table; } } + } } From 752af4ea605e1a793a989146d4cc803118844537 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen Date: Sun, 2 Feb 2025 00:01:04 +0100 Subject: [PATCH 5/6] fixed bugs --- .../api-cinema-challenge/DTO/TicketDTO.cs | 3 ++- .../api-cinema-challenge/Data/CinemaContext.cs | 3 ++- .../api-cinema-challenge/Endpoints/CinemaEndpoint.cs | 12 ++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs index 3b20103f..d72ba3da 100644 --- a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs +++ b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs @@ -1,5 +1,6 @@ using api_cinema_challenge.Models; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; namespace api_cinema_challenge.DTO { @@ -11,7 +12,7 @@ public class TicketDTO public int screenNumber { get; set; } public int customerID { get; set; } - + [JsonIgnore] public string customer { 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 0135298e..a8e0a77c 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -53,7 +53,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasOne(a => a.screen) - .WithMany(a => a.tickets); + .WithMany(a => a.tickets) + .HasForeignKey(a => a.screenId); modelBuilder.Entity() .HasOne(a => a.movie) diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs index fcf48351..392c2799 100644 --- a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -187,8 +187,8 @@ public static async Task CreateTicket(IRepository repo, int cus Ticket ticket = new Ticket { customerID = customerId, screenId = screenId }; repo.Insert(ticket); - //repo.Save(); - return TypedResults.Ok(new TicketDTO(ticket)); + repo.Save(); + return TypedResults.Ok(); @@ -225,14 +225,10 @@ public static async Task CreateScreen(IRepository repo, int cap { try { - Screen screen = new Screen(); - screen.capacity = capacity; - screen.screenNumber = screenNumber; - screen.startsAt = startsAt; - screen.movieId = movieId; + Screen screen = new Screen { capacity = capacity, movieId = movieId, createdAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), screenNumber = screenNumber, startsAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), updatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; repo.Insert(screen); repo.Save(); - return TypedResults.Ok(new ScreenDTO(screen)); + return TypedResults.Ok(); } catch (Exception e) { From 10e373228f1256159f2e59b74940c990be320227 Mon Sep 17 00:00:00 2001 From: Sander Rasmussen <102761818+sanderrasmussen@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:29:13 +0100 Subject: [PATCH 6/6] Add files via upload --- Screenshot 2025-01-22 100528.png | Bin 0 -> 93985 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Screenshot 2025-01-22 100528.png diff --git a/Screenshot 2025-01-22 100528.png b/Screenshot 2025-01-22 100528.png new file mode 100644 index 0000000000000000000000000000000000000000..ed31f695dd5ec83c0bef2d9678ad2e13bba320e1 GIT binary patch literal 93985 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 A`v3p{ literal 0 HcmV?d00001