diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/BookingDto.cs b/api-cinema-challenge/api-cinema-challenge/DTO/BookingDto.cs new file mode 100644 index 00000000..2ccf762a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/BookingDto.cs @@ -0,0 +1,8 @@ +public class BookingDto +{ + public int CustomerId { get; set; } + public int MovieId { get; set; } + public DateTime BookedAt { get; set; } + public string? CustomerName { get; set; } + public string? MovieTitle { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/CreateBookingDto.cs b/api-cinema-challenge/api-cinema-challenge/DTO/CreateBookingDto.cs new file mode 100644 index 00000000..ec19a75e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/CreateBookingDto.cs @@ -0,0 +1,5 @@ +public class CreateBookingDto +{ + public int CustomerId { get; set; } + public int MovieId { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/RegisterCustomerDto.cs b/api-cinema-challenge/api-cinema-challenge/DTO/RegisterCustomerDto.cs new file mode 100644 index 00000000..dd4defbb --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/RegisterCustomerDto.cs @@ -0,0 +1,7 @@ +public class RegisterCustomerDto +{ + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string Phonenumber { get; set; } = string.Empty; +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index ad4fe854..c43fbbfe 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -6,11 +6,17 @@ namespace api_cinema_challenge.Data public class CinemaContext : DbContext { private string _connectionString; + public DbSet Customers { get; set; } = null!; + public DbSet Movies { get; set; } = null!; + public DbSet Screenings { get; set; } = null!; + public DbSet Users { get; set; } = null!; + public DbSet Bookings { get; set; } = null!; + + public CinemaContext(DbContextOptions options) : base(options) { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; - this.Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -20,7 +26,29 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity() + .HasKey(b => new { b.CustomerId, b.MovieId }); + + modelBuilder.Entity() + .Property(c => c.CreatedAt) + .HasDefaultValueSql("CURRENT_TIMESTAMP"); - } + modelBuilder.Entity() + .Property(c => c.UpdatedAt) + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + modelBuilder.Entity() + .HasKey(b => new { b.CustomerId, b.MovieId }); + + modelBuilder.Entity() + .HasOne(b => b.Customer) + .WithMany(c => c.Bookings) + .HasForeignKey(b => b.CustomerId); + + modelBuilder.Entity() + .HasOne(b => b.Movie) + .WithMany(m => m.Bookings) + .HasForeignKey(b => b.MovieId); + } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Data/ER diagram.png b/api-cinema-challenge/api-cinema-challenge/Data/ER diagram.png new file mode 100644 index 00000000..dacfe420 Binary files /dev/null and b/api-cinema-challenge/api-cinema-challenge/Data/ER diagram.png differ diff --git a/api-cinema-challenge/api-cinema-challenge/Dockerfile b/api-cinema-challenge/api-cinema-challenge/Dockerfile new file mode 100644 index 00000000..dffbf425 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Dockerfile @@ -0,0 +1,29 @@ +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy csproj and restore dependencies +COPY *.csproj ./ +RUN dotnet restore + +# Copy everything else and build +COPY . . +RUN dotnet publish -c Release -o /app/publish + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +COPY --from=build /app/publish . + +# Expose port +EXPOSE 8080 + +# Run your app +ENTRYPOINT ["dotnet", "api-cinema-challenge.dll"] + + +# Create the image with +# sudo docker build -t dockerimage . + +# Run a container +# sudo docker run -p 8080:8080 -e ASPNETCORE_URLS="http://+:8080" -e DOTNET_ENVIRONMENT=Development dockerimage \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/BookingEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/BookingEndpoints.cs new file mode 100644 index 00000000..f07174eb --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/BookingEndpoints.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Mvc; + +public static class BookingEndpoints +{ + public static void MapBookingEndpoints(this WebApplication app) + { + app.MapGet("/bookings", async ([FromServices] IBookingRepository repo) => + { + var bookings = await repo.GetAllAsync(); + + var dtoList = bookings.Select(b => new BookingDto + { + CustomerId = b.CustomerId, + MovieId = b.MovieId, + BookedAt = b.BookedAt, + MovieTitle = b.Movie?.Title + }); + + return Results.Ok(dtoList); + }) + .WithTags("Bookings"); + + + app.MapPost("/bookings", async (CreateBookingDto dto, [FromServices] IBookingRepository repo) => + { + var booking = new Booking + { + CustomerId = dto.CustomerId, + MovieId = dto.MovieId + }; + + var createdBooking = await repo.CreateAsync(booking); + + var responseDto = new BookingDto + { + CustomerId = createdBooking.CustomerId, + MovieId = createdBooking.MovieId, + }; + + + return Results.Created( + $"/bookings/{booking.CustomerId}/{booking.MovieId}", + responseDto + ); + }) + .WithTags("Bookings"); + + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs new file mode 100644 index 00000000..9610482f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CustomerEndpoints.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +public static class CustomerEndpoints +{ + public static void MapCustomerEndpoints(this WebApplication app) + { + app.MapGet("/customers", [Authorize] async ([FromServices] ICustomerRepository repo) => + { + var customers = await repo.GetAllAsync(); + return Results.Ok(customers); + }) + .WithTags("Customers"); + + app.MapGet("/customers/{id}", [Authorize] async (int id, [FromServices] ICustomerRepository repo) => + { + var customer = await repo.GetByIdAsync(id); + return customer is not null ? Results.Ok(customer) : Results.NotFound(); + }) + .WithTags("Customers"); + + app.MapPost("/customers", [Authorize] async (Customer customer, [FromServices] ICustomerRepository repo) => + { + var createdCustomer = await repo.CreateAsync(customer); + return Results.Created($"/customers/{createdCustomer.Id}", createdCustomer); + }) + .WithTags("Customers"); + + app.MapPut("/customers/{id}", [Authorize] async (int id, Customer customer, [FromServices] ICustomerRepository repo) => + { + var updatedCustomer = await repo.UpdateAsync(id, customer); + return updatedCustomer is not null ? Results.Ok(updatedCustomer) : Results.NotFound(); + }) + .WithTags("Customers"); + + app.MapDelete("/customers/{id}", [Authorize] async (int id, [FromServices] ICustomerRepository repo) => + { + var deleted = await repo.DeleteAsync(id); + return deleted ? Results.Ok() : Results.NotFound(); + }) + .WithTags("Customers"); + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/LoginEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/LoginEndpoints.cs new file mode 100644 index 00000000..0cb27824 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/LoginEndpoints.cs @@ -0,0 +1,45 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Text; +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; + +public static class LoginEndpoints +{ + public static void MapLoginEndpoints(this WebApplication app, IConfiguration config) + { + var jwtSettings = config.GetSection("Jwt"); + + app.MapPost("/login", async (LoginRequest login, CinemaContext db) => + { + var user = await db.Customers.SingleOrDefaultAsync(u => u.Name == login.Name); + if (user == null || user.Password != login.Password) + return Results.Unauthorized(); + + var key = Encoding.UTF8.GetBytes(jwtSettings.GetValue("Key")); + + var tokenHandler = new JwtSecurityTokenHandler(); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new System.Security.Claims.ClaimsIdentity(new[] + { + new System.Security.Claims.Claim("id", user.Id.ToString()) + }), + + Expires = DateTime.UtcNow.AddMinutes(jwtSettings.GetValue("ExpiryMinutes")), + Issuer = jwtSettings["Issuer"], + Audience = jwtSettings["Audience"], + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + + + var token = tokenHandler.CreateToken(tokenDescriptor); + var jwt = tokenHandler.WriteToken(token); + + return Results.Ok(new { Token = jwt }); + }) + .WithTags("Authentication"); + + } + +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs new file mode 100644 index 00000000..a4646fbe --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/MovieEndpoints.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +public static class MovieEndpoints +{ + public static void MapMovieEndpoints(this WebApplication app) + { + app.MapGet("/movies", [Authorize] async ([FromServices] IMovieRepository repo) => + { + var movies = await repo.GetAllAsync(); + return Results.Ok(movies); + }) + .WithTags("Movies"); + + app.MapGet("/movies/{id}", [Authorize] async (int id, [FromServices] IMovieRepository repo) => + { + var movie = await repo.GetByIdAsync(id); + return movie is not null ? Results.Ok(movie) : Results.NotFound(); + }) + .WithTags("Movies"); + + app.MapPost("/movies", [Authorize] async (Movie movie, [FromServices] IMovieRepository repo) => + { + var createdMovie = await repo.CreateAsync(movie); + return Results.Created($"/movies/{createdMovie.Id}", createdMovie); + }) + .WithTags("Movies"); + + app.MapPut("/movies/{id}", [Authorize] async (int id, Movie movie, [FromServices] IMovieRepository repo) => + { + var updatedMovie = await repo.UpdateAsync(id, movie); + return updatedMovie is not null ? Results.Ok(updatedMovie) : Results.NotFound(); + }) + .WithTags("Movies"); + + app.MapDelete("/movies/{id}", [Authorize] async (int id, [FromServices] IMovieRepository repo) => + { + var deleted = await repo.DeleteAsync(id); + return deleted ? Results.Ok() : Results.NotFound(); + }) + .WithTags("Movies"); + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/RegisterEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/RegisterEndpoints.cs new file mode 100644 index 00000000..9f626d91 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/RegisterEndpoints.cs @@ -0,0 +1,26 @@ +using api_cinema_challenge.Data; +using Microsoft.AspNetCore.Mvc; + +public static class RegisterEndpoints +{ + public static void MapRegisterEndpoints(this WebApplication app) + { + app.MapPost("/register", async (RegisterCustomerDto dto, CinemaContext db) => + { + var customer = new Customer + { + Name = dto.Name, + Email = dto.Email, + Password = dto.Password, + Phonenumber = dto.Phonenumber, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + db.Customers.Add(customer); + await db.SaveChangesAsync(); + + return Results.Ok(new { customer.Id, customer.Name, customer.Email }); + }); + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs new file mode 100644 index 00000000..6b1c39b6 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/ScreeningEndpoints.cs @@ -0,0 +1,22 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +public static class ScreeningEndpoints +{ + public static void MapScreeningEndpoints(this WebApplication app) + { + app.MapGet("/screenings", [Authorize] async ([FromServices] IScreeningRepository repo) => + { + var screening = await repo.GetAllAsync(); + return Results.Ok(screening); + }) + .WithTags("Screenings"); + + app.MapPost("/screenings", [Authorize] async (Screening screening, [FromServices] IScreeningRepository repo) => + { + var createdMovie = await repo.CreateAsync(screening); + return Results.Created($"/screenings/{createdMovie.Id}", createdMovie); + }) + .WithTags("Screenings"); + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.Designer.cs new file mode 100644 index 00000000..124f75ef --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.Designer.cs @@ -0,0 +1,99 @@ +// +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("20250825083406_movieTable")] + partial class movieTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.cs new file mode 100644 index 00000000..cb7480cb --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825083406_movieTable.cs @@ -0,0 +1,61 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class movieTable : 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, defaultValueSql: "CURRENT_TIMESTAMP"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP") + }, + 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: "integer", 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_Movies", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Customers"); + + migrationBuilder.DropTable( + name: "Movies"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.Designer.cs new file mode 100644 index 00000000..4e8f11bf --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.Designer.cs @@ -0,0 +1,100 @@ +// +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("20250825084022_ChangeMovieTitleToString")] + partial class ChangeMovieTitleToString + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.cs new file mode 100644 index 00000000..d8eaa3cc --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825084022_ChangeMovieTitleToString.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class ChangeMovieTitleToString : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Title", + table: "Movies", + type: "text", + nullable: false, + oldClrType: typeof(int), + oldType: "integer"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Title", + table: "Movies", + type: "integer", + nullable: false, + oldClrType: typeof(string), + oldType: "text"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.Designer.cs new file mode 100644 index 00000000..1d94c09f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.Designer.cs @@ -0,0 +1,126 @@ +// +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("20250825085423_screeningsTable")] + partial class screeningsTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("ScreeningTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Theater") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Screenings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.cs new file mode 100644 index 00000000..3e159e8e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085423_screeningsTable.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class screeningsTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Screenings", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + MovieId = table.Column(type: "integer", nullable: false), + ScreeningTime = table.Column(type: "timestamp with time zone", nullable: false), + Theater = table.Column(type: "text", nullable: false), + Price = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Screenings", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Screenings"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.Designer.cs new file mode 100644 index 00000000..d6e9fdf2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.Designer.cs @@ -0,0 +1,119 @@ +// +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("20250825085705_screeningsTable2")] + partial class screeningsTable2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Screenings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.cs new file mode 100644 index 00000000..ebb8b56b --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825085705_screeningsTable2.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class screeningsTable2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Price", + table: "Screenings"); + + migrationBuilder.DropColumn( + name: "Theater", + table: "Screenings"); + + migrationBuilder.RenameColumn( + name: "ScreeningTime", + table: "Screenings", + newName: "StartsAt"); + + migrationBuilder.RenameColumn( + name: "MovieId", + table: "Screenings", + newName: "ScreenNumber"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "StartsAt", + table: "Screenings", + newName: "ScreeningTime"); + + migrationBuilder.RenameColumn( + name: "ScreenNumber", + table: "Screenings", + newName: "MovieId"); + + migrationBuilder.AddColumn( + name: "Price", + table: "Screenings", + type: "numeric", + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "Theater", + table: "Screenings", + type: "text", + nullable: false, + defaultValue: ""); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825094651_screeningsTable3.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825094651_screeningsTable3.Designer.cs new file mode 100644 index 00000000..2d4d6b86 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825094651_screeningsTable3.Designer.cs @@ -0,0 +1,138 @@ +// +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("20250825094651_screeningsTable3")] + partial class screeningsTable3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("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/20250825094651_screeningsTable3.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825094651_screeningsTable3.cs new file mode 100644 index 00000000..134f05d4 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825094651_screeningsTable3.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class screeningsTable3 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Capacity", + table: "Screenings", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "MovieId", + table: "Screenings", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Screenings_MovieId", + table: "Screenings", + column: "MovieId"); + + migrationBuilder.AddForeignKey( + name: "FK_Screenings_Movies_MovieId", + table: "Screenings", + column: "MovieId", + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Screenings_Movies_MovieId", + table: "Screenings"); + + migrationBuilder.DropIndex( + name: "IX_Screenings_MovieId", + table: "Screenings"); + + migrationBuilder.DropColumn( + name: "Capacity", + table: "Screenings"); + + migrationBuilder.DropColumn( + name: "MovieId", + table: "Screenings"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825105802_userTable.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825105802_userTable.Designer.cs new file mode 100644 index 00000000..7546b4a5 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825105802_userTable.Designer.cs @@ -0,0 +1,159 @@ +// +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("20250825105802_userTable")] + partial class userTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("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/20250825105802_userTable.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825105802_userTable.cs new file mode 100644 index 00000000..33fed9cc --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825105802_userTable.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class userTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Username = table.Column(type: "text", nullable: false), + PasswordHash = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.Designer.cs new file mode 100644 index 00000000..19ac9a7c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.Designer.cs @@ -0,0 +1,206 @@ +// +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("20250825114838_bookingTable")] + partial class bookingTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Booking"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.cs new file mode 100644 index 00000000..4698ca6d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825114838_bookingTable.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class bookingTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Booking", + columns: table => new + { + CustomerId = table.Column(type: "integer", nullable: false), + MovieId = table.Column(type: "integer", nullable: false), + BookedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Booking", x => new { x.CustomerId, x.MovieId }); + table.ForeignKey( + name: "FK_Booking_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Booking_Movies_MovieId", + column: x => x.MovieId, + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Booking_MovieId", + table: "Booking", + column: "MovieId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Booking"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.Designer.cs new file mode 100644 index 00000000..9f1b42fd --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.Designer.cs @@ -0,0 +1,214 @@ +// +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("20250825120320_removedUserTable")] + partial class removedUserTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Booking"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.cs new file mode 100644 index 00000000..582bb381 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120320_removedUserTable.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class removedUserTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PasswordHash", + table: "Customers", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "Username", + table: "Customers", + type: "text", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PasswordHash", + table: "Customers"); + + migrationBuilder.DropColumn( + name: "Username", + table: "Customers"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.Designer.cs new file mode 100644 index 00000000..ba445f7d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.Designer.cs @@ -0,0 +1,214 @@ +// +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("20250825120931_smallchange")] + partial class smallchange + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Booking"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.cs new file mode 100644 index 00000000..a081bede --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825120931_smallchange.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class smallchange : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "PasswordHash", + table: "Customers", + newName: "Password"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Password", + table: "Customers", + newName: "PasswordHash"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.Designer.cs new file mode 100644 index 00000000..b3024b9c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.Designer.cs @@ -0,0 +1,214 @@ +// +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("20250825121333_smallchange3")] + partial class smallchange3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Booking"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phonenumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.cs new file mode 100644 index 00000000..bb10b54c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825121333_smallchange3.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class smallchange3 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Phone", + table: "Customers", + newName: "Phonenumber"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Phonenumber", + table: "Customers", + newName: "Phone"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.Designer.cs new file mode 100644 index 00000000..a36352e1 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.Designer.cs @@ -0,0 +1,214 @@ +// +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("20250825124543_smallchange4")] + partial class smallchange4 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Bookings"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phonenumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.cs new file mode 100644 index 00000000..408a2df0 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250825124543_smallchange4.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class smallchange4 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Booking_Customers_CustomerId", + table: "Booking"); + + migrationBuilder.DropForeignKey( + name: "FK_Booking_Movies_MovieId", + table: "Booking"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Booking", + table: "Booking"); + + migrationBuilder.RenameTable( + name: "Booking", + newName: "Bookings"); + + migrationBuilder.RenameIndex( + name: "IX_Booking_MovieId", + table: "Bookings", + newName: "IX_Bookings_MovieId"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Bookings", + table: "Bookings", + columns: new[] { "CustomerId", "MovieId" }); + + migrationBuilder.AddForeignKey( + name: "FK_Bookings_Customers_CustomerId", + table: "Bookings", + column: "CustomerId", + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Bookings_Movies_MovieId", + table: "Bookings", + column: "MovieId", + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Bookings_Customers_CustomerId", + table: "Bookings"); + + migrationBuilder.DropForeignKey( + name: "FK_Bookings_Movies_MovieId", + table: "Bookings"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Bookings", + table: "Bookings"); + + migrationBuilder.RenameTable( + name: "Bookings", + newName: "Booking"); + + migrationBuilder.RenameIndex( + name: "IX_Bookings_MovieId", + table: "Booking", + newName: "IX_Booking_MovieId"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Booking", + table: "Booking", + columns: new[] { "CustomerId", "MovieId" }); + + migrationBuilder.AddForeignKey( + name: "FK_Booking_Customers_CustomerId", + table: "Booking", + column: "CustomerId", + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_Booking_Movies_MovieId", + table: "Booking", + column: "MovieId", + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} 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..1da9d55f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,211 @@ +// +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.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Booking", b => + { + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("BookedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CustomerId", "MovieId"); + + b.HasIndex("MovieId"); + + b.ToTable("Bookings"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Password") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phonenumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("Username") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Booking", b => + { + b.HasOne("Customer", "Customer") + .WithMany("Bookings") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Movie", "Movie") + .WithMany("Bookings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Screening", b => + { + b.HasOne("Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("Customer", b => + { + b.Navigation("Bookings"); + }); + + modelBuilder.Entity("Movie", b => + { + b.Navigation("Bookings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Booking.cs b/api-cinema-challenge/api-cinema-challenge/Models/Booking.cs new file mode 100644 index 00000000..d27d9e01 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Booking.cs @@ -0,0 +1,10 @@ +public class Booking +{ + public int CustomerId { get; set; } + public Customer Customer { get; set; } = null!; + + public int MovieId { get; set; } + public Movie Movie { get; set; } = null!; + + public DateTime BookedAt { get; set; } = DateTime.UtcNow; +} \ 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 new file mode 100644 index 00000000..972846d0 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,18 @@ +public class Customer +{ + public int Id { get; set; } + + // Login-related + public string Username { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + + // Profile-related + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string Phonenumber { get; set; } = string.Empty; + + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + + public List Bookings { get; set; } = new(); +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/LoginRequest.cs b/api-cinema-challenge/api-cinema-challenge/Models/LoginRequest.cs new file mode 100644 index 00000000..7240ce14 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/LoginRequest.cs @@ -0,0 +1,5 @@ +public class LoginRequest +{ + public string Name { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs new file mode 100644 index 00000000..4beb17bd --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,11 @@ +public class Movie +{ + public int Id { get; set; } + public string Title { get; set; } = string.Empty; + public string Rating { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string RuntimeMins { get; set; } = string.Empty; + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; + public List Bookings { get; set; } = new(); +} 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..6813d101 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs @@ -0,0 +1,9 @@ +public class Screening +{ + public int Id { get; set; } + public int MovieId { get; set; } + public Movie Movie { get; set; } = null!; // Navigation property! + public int ScreenNumber { get; set; } + public int Capacity { get; set; } + public DateTime StartsAt { get; set; } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/User.cs b/api-cinema-challenge/api-cinema-challenge/Models/User.cs new file mode 100644 index 00000000..422b2287 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/User.cs @@ -0,0 +1,6 @@ +public class User +{ + public int Id { get; set; } + public string Username { get; set; } = string.Empty; + public string PasswordHash { get; set; } = string.Empty; //not safe :( +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..670bd1e6 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,15 +1,85 @@ using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; var builder = WebApplication.CreateBuilder(args); +builder.Services.AddDbContext(options => + options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnectionString"))); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + // Add services to the container. builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); -builder.Services.AddDbContext(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo + { + Title = "API Cinema Challenge", + Version = "v1" + }); + + // JWT config + c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Name = "Authorization", + Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT", + In = Microsoft.OpenApi.Models.ParameterLocation.Header, + Description = "Enter 'Bearer' [space] and then your valid token." + }); + + c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement + { + { + new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Reference = new Microsoft.OpenApi.Models.OpenApiReference + { + Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } + }); +}); + + +// JWT config +var jwtSettings = builder.Configuration.GetSection("Jwt"); +var key = Encoding.ASCII.GetBytes(jwtSettings.GetValue("Key")); +builder.Services.AddAuthentication(options => +{ + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) +.AddJwtBearer(options => +{ + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = jwtSettings["Issuer"], + ValidAudience = jwtSettings["Audience"], + IssuerSigningKey = new SymmetricSecurityKey(key) + }; +}); + + +builder.Services.AddAuthorization(); var app = builder.Build(); -// Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) { app.UseSwagger(); @@ -17,4 +87,15 @@ } app.UseHttpsRedirection(); +app.UseAuthentication(); +app.UseAuthorization(); + +//Endpoints into swagger +app.MapCustomerEndpoints(); +app.MapMovieEndpoints(); +app.MapScreeningEndpoints(); +app.MapRegisterEndpoints(); +app.MapBookingEndpoints(); +app.MapLoginEndpoints(builder.Configuration); + app.Run(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/BookingRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/BookingRepository.cs new file mode 100644 index 00000000..d5e6c619 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/BookingRepository.cs @@ -0,0 +1,28 @@ +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; + + +public class BookingRepository : IBookingRepository +{ + private readonly CinemaContext _context; + + public BookingRepository(CinemaContext context) + { + _context = context; + } + + + public async Task> GetAllAsync() + { + return await _context.Bookings + .Include(s => s.Movie) + .ToListAsync(); + } + + public async Task CreateAsync(Booking bookings) + { + _context.Bookings.Add(bookings); + await _context.SaveChangesAsync(); + return bookings; + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/CustomerRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/CustomerRepository.cs new file mode 100644 index 00000000..715278b6 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/CustomerRepository.cs @@ -0,0 +1,54 @@ +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; + + +public class CustomerRepository : ICustomerRepository +{ + private readonly CinemaContext _context; + + public CustomerRepository(CinemaContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.Customers.ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.Customers.FindAsync(id); + } + + public async Task CreateAsync(Customer customer) + { + _context.Customers.Add(customer); + await _context.SaveChangesAsync(); + return customer; + } + + public async Task UpdateAsync(int id, Customer customer) + { + var existingCustomer = await _context.Customers.FindAsync(id); + if (existingCustomer == null) return null; + + existingCustomer.Name = customer.Name; + existingCustomer.Email = customer.Email; + existingCustomer.Phonenumber = customer.Phonenumber; + existingCustomer.UpdatedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + return existingCustomer; + } + + public async Task DeleteAsync(int id) + { + var customer = await _context.Customers.FindAsync(id); + if (customer == null) return false; + + _context.Customers.Remove(customer); + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IBookingRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IBookingRepository.cs new file mode 100644 index 00000000..3fe9a910 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IBookingRepository.cs @@ -0,0 +1,5 @@ +public interface IBookingRepository +{ + Task> GetAllAsync(); + Task CreateAsync(Booking screening); +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/ICustomerRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/ICustomerRepository.cs new file mode 100644 index 00000000..035d528d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/ICustomerRepository.cs @@ -0,0 +1,8 @@ +public interface ICustomerRepository +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(Customer customer); + Task UpdateAsync(int id, Customer customer); + Task DeleteAsync(int id); +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IMovieRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IMovieRepository.cs new file mode 100644 index 00000000..3128585f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IMovieRepository.cs @@ -0,0 +1,8 @@ +public interface IMovieRepository +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(Movie customer); + Task UpdateAsync(int id, Movie customer); + Task DeleteAsync(int id); +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IScreeningRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IScreeningRepository.cs new file mode 100644 index 00000000..5b32e6fc --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IScreeningRepository.cs @@ -0,0 +1,5 @@ +public interface IScreeningRepository +{ + Task> GetAllAsync(); + Task CreateAsync(Screening screening); +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/MovieRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/MovieRepository.cs new file mode 100644 index 00000000..45384d40 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/MovieRepository.cs @@ -0,0 +1,54 @@ +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; + + +public class MovieRepository : IMovieRepository +{ + private readonly CinemaContext _context; + + public MovieRepository(CinemaContext context) + { + _context = context; + } + + public async Task> GetAllAsync() + { + return await _context.Movies.ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await _context.Movies.FindAsync(id); + } + + public async Task CreateAsync(Movie movie) + { + _context.Movies.Add(movie); + await _context.SaveChangesAsync(); + return movie; + } + + public async Task UpdateAsync(int id, Movie movie) + { + var existingCustomer = await _context.Movies.FindAsync(id); + if (existingCustomer == null) return null; + + existingCustomer.Title = movie.Title; + existingCustomer.Rating = movie.Rating; + existingCustomer.RuntimeMins = movie.RuntimeMins; + existingCustomer.UpdatedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + return existingCustomer; + } + + public async Task DeleteAsync(int id) + { + var customer = await _context.Customers.FindAsync(id); + if (customer == null) return false; + + _context.Customers.Remove(customer); + await _context.SaveChangesAsync(); + return true; + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/ScreeningRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/ScreeningRepository.cs new file mode 100644 index 00000000..71674cc4 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/ScreeningRepository.cs @@ -0,0 +1,28 @@ +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; + + +public class ScreeningRepository : IScreeningRepository +{ + private readonly CinemaContext _context; + + public ScreeningRepository(CinemaContext context) + { + _context = context; + } + + + public async Task> GetAllAsync() + { + return await _context.Screenings + .Include(s => s.Movie) + .ToListAsync(); + } + + public async Task CreateAsync(Screening screening) + { + _context.Screenings.Add(screening); + await _context.SaveChangesAsync(); + return screening; + } +} 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 11e5c66b..79b7345e 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -15,6 +15,7 @@ + @@ -25,6 +26,7 @@ + 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