diff --git a/api-cinema-challenge/api-cinema-challenge.sln b/api-cinema-challenge/api-cinema-challenge.sln index 9cd490f5..03981e44 100644 --- a/api-cinema-challenge/api-cinema-challenge.sln +++ b/api-cinema-challenge/api-cinema-challenge.sln @@ -8,6 +8,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3C371BAA-344D-4C8A-AF08-7829816D726F}" ProjectSection(SolutionItems) = preProject ..\.gitignore = ..\.gitignore + api-cinema-challenge\ER Cinema Challenge.png = api-cinema-challenge\ER Cinema Challenge.png ..\README.md = ..\README.md EndProjectSection EndProject diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs new file mode 100644 index 00000000..31f0f476 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/CustomerDTO.cs @@ -0,0 +1,13 @@ +namespace api_cinema_challenge.DTOs +{ + public class CustomerDTO + { + public int Id { get; set; } + public string Name { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs new file mode 100644 index 00000000..ff05792f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/MovieDTO.cs @@ -0,0 +1,13 @@ +namespace api_cinema_challenge.DTOs +{ + public class MovieDTO + { + public int Id { get; set; } + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public int RuntimeMinutes { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedScreeningDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedScreeningDTO.cs new file mode 100644 index 00000000..7483d2c7 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedScreeningDTO.cs @@ -0,0 +1,12 @@ +namespace api_cinema_challenge.DTOs.NestedDTOs +{ + public class NestedScreeningDTO + { + public int ScreenNumber { get; set; } + public DateTime StartsAt { get; set; } + public int Capacity { get; set; } + public MovieDTO Movie { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedTicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedTicketDTO.cs new file mode 100644 index 00000000..bfcb0e7b --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/NestedDTOs/NestedTicketDTO.cs @@ -0,0 +1,11 @@ +namespace api_cinema_challenge.DTOs.NestedDTOs +{ + public class NestedTicketDTO + { + public int NumSeats { get; set; } + public CustomerDTO Customer { get; set; } + public NestedScreeningDTO Screening { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs new file mode 100644 index 00000000..663106a3 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/ScreeningDTO.cs @@ -0,0 +1,12 @@ +namespace api_cinema_challenge.DTOs +{ + public class ScreeningDTO + { + public int Id { get; set; } + public int ScreenNumber { get; set; } + public int Capacity { get; set; } + public DateTime StartsAt { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs new file mode 100644 index 00000000..ca794d65 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTOs/TicketDTO.cs @@ -0,0 +1,20 @@ +namespace api_cinema_challenge.DTOs +{ + public class TicketDTO + { + public int Id { get; set; } + public int NumSeats { get; set; } + public CustomerDTO Customer { get; set; } + public ScreeningDTO Screening { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } + + public class MinimalTicketDTO + { + public int Id { get; set; } + public int NumSeats { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index ad4fe854..c4a77ae9 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; namespace api_cinema_challenge.Data @@ -6,7 +7,7 @@ namespace api_cinema_challenge.Data public class CinemaContext : DbContext { private string _connectionString; - public CinemaContext(DbContextOptions options) : base(options) + public CinemaContext() { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; @@ -22,5 +23,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { } + + public DbSet Customers { get; set; } + public DbSet Movies { get; set; } + public DbSet Screenings { get; set; } + public DbSet Tickets { get; set; } } } diff --git a/api-cinema-challenge/api-cinema-challenge/Data/Seeding.cs b/api-cinema-challenge/api-cinema-challenge/Data/Seeding.cs new file mode 100644 index 00000000..3a6612e0 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Data/Seeding.cs @@ -0,0 +1,49 @@ +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.Data +{ + public static class Seeding + { + public async static void SeedDatabase(this WebApplication app) + { + using (var db = new CinemaContext()) + { + if(!db.Customers.Any()) + { + db.Add(new Customer() { Name = "Nigel", Phone = "40506070", Email = "nigel@boolean.co.uk", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Customer() { Name = "Dave", Phone = "60503020", Email = "dave@boolean.co.uk", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Customer() { Name = "Sandro", Phone = "10206090", Email = "sandro@espana.co.uk", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Customer() { Name = "Lisa", Phone = "40506070", Email = "lisa@email.com", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + await db.SaveChangesAsync(); + } + if (!db.Movies.Any()) + { + db.Add(new Movie() { Title = "Scream", Rating = "PG-16", Description = "Big Masks equals big entertainment", RuntimeMinutes = 115, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Movie() { Title = "Kill Bill", Rating = "PG-16", Description = "Big Swords equals big entertainment", RuntimeMinutes = 94, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Movie() { Title = "Moana", Rating = "PG-7", Description = "Large Waves equals large entertainment", RuntimeMinutes = 98, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + await db.SaveChangesAsync(); + } + if (!db.Screenings.Any()) + { + db.Add(new Screening() { ScreenNumber = 1, StartsAt = DateTime.SpecifyKind(new DateTime(2025, 1, 30, 19, 30, 0), DateTimeKind.Utc), Capacity = 60, MovieId = 2, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Screening() { ScreenNumber = 2, StartsAt = DateTime.SpecifyKind(new DateTime(2025, 1, 30, 19, 30, 0), DateTimeKind.Utc), Capacity = 50, MovieId = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Screening() { ScreenNumber = 3, StartsAt = DateTime.SpecifyKind(new DateTime(2025, 1, 30, 19, 30, 0), DateTimeKind.Utc), Capacity = 40, MovieId = 3, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + db.Add(new Screening() { ScreenNumber = 1, StartsAt = DateTime.SpecifyKind(new DateTime(2025, 1, 31, 20, 30, 0), DateTimeKind.Utc), Capacity = 50, MovieId = 2, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); + await db.SaveChangesAsync(); + } + if (!db.Tickets.Any()) + { + db.Add(new Ticket() { NumSeats = 3, CustomerId = 1, ScreeningId = 2, }); + db.Add(new Ticket() { NumSeats = 5, CustomerId = 2, ScreeningId = 1, }); + db.Add(new Ticket() { NumSeats = 3, CustomerId = 3, ScreeningId = 3, }); + db.Add(new Ticket() { NumSeats = 2, CustomerId = 4, ScreeningId = 4, }); + db.Add(new Ticket() { NumSeats = 3, CustomerId = 2, ScreeningId = 3, }); + db.Add(new Ticket() { NumSeats = 2, CustomerId = 2, ScreeningId = 4, }); + await db.SaveChangesAsync(); + } + + + } + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/ER Cinema Challenge.png b/api-cinema-challenge/api-cinema-challenge/ER Cinema Challenge.png new file mode 100644 index 00000000..35995ef8 Binary files /dev/null and b/api-cinema-challenge/api-cinema-challenge/ER Cinema Challenge.png differ diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaAPI.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaAPI.cs new file mode 100644 index 00000000..fb752312 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaAPI.cs @@ -0,0 +1,259 @@ + +using api_cinema_challenge.DTOs; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using api_cinema_challenge.Request; +using api_cinema_challenge.Response; +using AutoMapper; + +namespace api_cinema_challenge.Endpoints +{ + public static class CinemaAPI + { + public static void ConfigureCinemaAPI(this WebApplication app) + { + var customerGroup = app.MapGroup("/customers"); + customerGroup.MapGet("/", GetAllCustomers); + customerGroup.MapPost("/", CreateCustomer); + customerGroup.MapPut("/{id}", EditCustomer); + customerGroup.MapDelete("/{id}", DeleteCustomer); + customerGroup.MapGet("/{customerId}/screenings/{screeningId}", GetAllTickets); + customerGroup.MapPost("/{customerId}/screenings/{screeningId}", CreateTicket); + + var movieGroup = app.MapGroup("/movies"); + movieGroup.MapPost("/", CreateMovie); + movieGroup.MapGet("/", GetAllMovies); + movieGroup.MapPut("/{id}", EditMovie); + movieGroup.MapDelete("/{id}", DeleteMovie); + movieGroup.MapGet("/{id}/screenings", GetAllScreenings); + movieGroup.MapPost("/{id}/screenings", CreateScreening); + } + + private static async Task GetAllMovies(IRepository repository, IMapper mapper) + { + var movies = await repository.GetAll(); + var response = mapper.Map>(movies); + return TypedResults.Ok(new Response>("Success", response)); + } + + private static async Task CreateMovie(IRepository movieRepository, + IRepository screeningRepository, + MoviePost payload, + IMapper mapper) + { + if(payload == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Payload at fault"))); + } + if(payload.Title == null || + payload.RuntimeMinutes == null || + payload.Description == null || + payload.Rating == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Missing Title, RuntimeMinutes, Description or Rating"))); + } + Movie movie = new Movie() + { + Title = payload.Title!, + Rating = payload.Rating, + Description = payload.Description, + RuntimeMinutes = (int)payload.RuntimeMinutes, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now + }; + var inserted = await movieRepository.Insert(movie); + + if(payload.screenings.Count != 0) + { + foreach(ScreeningPost screening in payload.screenings) { + await screeningRepository.Insert( + new Screening() + { + ScreenNumber = screening.ScreenNumber, + StartsAt = screening.StartsAt, + Capacity = screening.Capacity, + MovieId = inserted.Id, + Movie = inserted, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Tickets = new List() + }); + } + } + return TypedResults.Created( + $"https://localhost:7010/movies/{inserted.Id}", + new Response("Success", mapper.Map(inserted))); + } + + private static async Task EditMovie(IRepository repository, int id, MoviePost payload, IMapper mapper) + { + if(payload == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Payload at fault"))); + } + Movie movie = repository.GetById(id).Result; + if(movie == null) + { + return TypedResults.NotFound(new Response("Error", new Error("Movie not Found"))); + } + movie.Title = payload.Title ?? movie.Title; + movie.Rating = payload.Rating ?? movie.Rating; + movie.Description = payload.Description ?? movie.Description; + movie.RuntimeMinutes = payload.RuntimeMinutes ?? movie.RuntimeMinutes; + movie.UpdatedAt = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Unspecified); + + await repository.Update(movie); + return TypedResults.Ok(new Response("Success", mapper.Map(movie))); + + } + + private static async Task DeleteMovie(IRepository repository, int id, IMapper mapper) + { + Movie movie = repository.GetById(id).Result; + if(movie == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Movie not found"))); + } + await repository.Delete(id); + return TypedResults.Ok(new Response("Success", mapper.Map(movie))); + } + + private static async Task GetAllCustomers(IRepository repository, IMapper mapper) + { + var customers = await repository.GetAll(); + var response = mapper.Map>(customers); + return TypedResults.Ok(new Response>("Success", response)); + } + + private static async Task CreateCustomer(IRepository repository, CustomerPost payload, IMapper mapper) + { + if (payload == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Payload at fault"))); + } + Customer customer = new Customer() + { + Name = payload.Name, + Phone = payload.Phone, + Email = payload.Email, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now + }; + var inserted = await repository.Insert(customer); + return TypedResults.Created($"https://localhost:7010/movies/{inserted.Id}", + new Response("Success", mapper.Map(inserted))); + } + + private static async Task EditCustomer(IRepository repository, int id, CustomerPost payload, IMapper mapper) + { + if (payload == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Payload at fault"))); + } + Customer customer = repository.GetById(id).Result; + customer.Name = payload.Name; + customer.Phone = payload.Phone; + customer.Email = payload.Email; + customer.UpdatedAt = DateTime.Now; + await repository.Update(customer); + return TypedResults.Ok(new Response("Success", mapper.Map(customer))); + } + + private static async Task DeleteCustomer(IRepository repository, int id, IMapper mapper) + { + Customer customer = repository.GetById(id).Result; + if (customer == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Customer not found"))); + } + await repository.Delete(id); + return TypedResults.Ok(new Response("Success", mapper.Map(customer))); + } + + /** + * TODO + * Get all screenings for movie + */ + + private static async Task GetAllScreenings(IRepository repository, int id, IMapper mapper) + { + List screenings = repository.GetQueryable() + .Where(x => x.MovieId == id) + .ToList(); + var response = mapper.Map>(screenings); + return TypedResults.Ok(new Response>("Success", response)); + } + + + /** + * TODO + * Integrate Id to request + */ + private static async Task CreateScreening(IRepository repository, int id, ScreeningPost payload, IMapper mapper) + { + { + if (payload == null) + { + return TypedResults.BadRequest(new Response("Error", new Error("Payload at fault"))); + } + Screening screening = new Screening() + { + ScreenNumber = payload.ScreenNumber, + StartsAt = payload.StartsAt, + Capacity = payload.Capacity, + MovieId = id, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + var inserted = await repository.Insert(screening); + return TypedResults.Created($"https://localhost:7010/movies/{inserted.Id}", + new Response("Success", mapper.Map(inserted))); + } + } + + private static async Task GetAllTickets(IRepository repository, int customerId, int screeningId, IMapper mapper) + { + List tickets = repository.GetQueryable() + .Where(x => x.CustomerId == customerId) + .Where(x => x.ScreeningId == screeningId) + .ToList(); + if(tickets.Count == 0) + { + return TypedResults.Ok(new Response("Error", new Error($"Customer {customerId} has no tickets for Screening {screeningId}"))); + } + return TypedResults.Ok(new Response>("Success", mapper.Map>(tickets))); + } + + private static async Task CreateTicket(IRepository ticketRepository, + IRepository screeningRepository, + IRepository customerRepository, + TicketPost payload, + int customerId, + int screeningId, + IMapper mapper) + { + Customer customer = customerRepository.GetById(customerId).Result; + Screening screening = screeningRepository.GetById(screeningId).Result; + if (screening == null) + { + return TypedResults.NotFound(new Response("Error", new Error("Screening was not found"))); + } + if (customer == null) + { + return TypedResults.NotFound(new Response("Error", new Error("Customer was not found"))); + } + Ticket ticket = new Ticket() + { + NumSeats = payload.NumSeats, + CustomerId = customerId, + ScreeningId = screeningId, + CreatedAt = DateTime.Now, + UpdatedAt = DateTime.Now, + Customer = customer, + Screening = screening + }; + await ticketRepository.Insert(ticket); + return TypedResults.Ok(new Response("Success", mapper.Map(ticket))); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.Designer.cs new file mode 100644 index 00000000..09c5ab38 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.Designer.cs @@ -0,0 +1,213 @@ +// +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("20250128110833_FirstMigration")] + partial class FirstMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text"); + + b.Property("RuntimeMinutes") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("NumSeats") + .HasColumnType("integer"); + + b.Property("ScreeningId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ScreeningId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany("Screenings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "Customer") + .WithMany("Tickets") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screening", "Screening") + .WithMany("Tickets") + .HasForeignKey("ScreeningId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Screening"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Navigation("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Navigation("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.cs new file mode 100644 index 00000000..8f12920c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250128110833_FirstMigration.cs @@ -0,0 +1,135 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class FirstMigration : 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), + Phone = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + created_at = table.Column(type: "timestamp", nullable: false), + updated_at = table.Column(type: "timestamp", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Customers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Movies", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Title = table.Column(type: "text", nullable: false), + Rating = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false), + RuntimeMinutes = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp", nullable: false), + updated_at = table.Column(type: "timestamp", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Movies", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Screenings", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ScreenNumber = table.Column(type: "integer", nullable: false), + StartsAt = table.Column(type: "timestamp with time zone", nullable: false), + Capacity = table.Column(type: "integer", nullable: false), + MovieId = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp", nullable: false), + updated_at = table.Column(type: "timestamp", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Screenings", x => x.Id); + table.ForeignKey( + name: "FK_Screenings_Movies_MovieId", + column: x => x.MovieId, + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Tickets", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + NumSeats = table.Column(type: "integer", nullable: false), + CustomerId = table.Column(type: "integer", nullable: false), + ScreeningId = table.Column(type: "integer", nullable: false), + created_at = table.Column(type: "timestamp", nullable: false), + updated_at = table.Column(type: "timestamp", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tickets", x => x.Id); + table.ForeignKey( + name: "FK_Tickets_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Tickets_Screenings_ScreeningId", + column: x => x.ScreeningId, + principalTable: "Screenings", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Screenings_MovieId", + table: "Screenings", + column: "MovieId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_CustomerId", + table: "Tickets", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_ScreeningId", + table: "Tickets", + column: "ScreeningId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tickets"); + + migrationBuilder.DropTable( + name: "Customers"); + + migrationBuilder.DropTable( + name: "Screenings"); + + migrationBuilder.DropTable( + name: "Movies"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs new file mode 100644 index 00000000..2f787140 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,210 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + partial class CinemaContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text"); + + b.Property("RuntimeMinutes") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("MovieId") + .HasColumnType("integer"); + + b.Property("ScreenNumber") + .HasColumnType("integer"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp") + .HasColumnName("created_at"); + + b.Property("CustomerId") + .HasColumnType("integer"); + + b.Property("NumSeats") + .HasColumnType("integer"); + + b.Property("ScreeningId") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ScreeningId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", "Movie") + .WithMany("Screenings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "Customer") + .WithMany("Tickets") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screening", "Screening") + .WithMany("Tickets") + .HasForeignKey("ScreeningId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Screening"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Navigation("Screenings"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screening", 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..b3fc73d8 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + public class Customer + { + public int Id { get; set; } + public string Name { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + + [Column("created_at", TypeName = "timestamp")] + public DateTime CreatedAt { get; set; } + [Column("updated_at", TypeName = "timestamp")] + public DateTime UpdatedAt { get; set; } + + public List Tickets { 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..cc935ef1 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + public class Movie + { + public int Id { get; set; } + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public int RuntimeMinutes { get; set; } + + [Column("created_at", TypeName = "timestamp")] + public DateTime CreatedAt { get; set; } + [Column("updated_at", TypeName = "timestamp")] + public DateTime UpdatedAt { get; set; } + + public List Screenings { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs new file mode 100644 index 00000000..654ece3a --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screening.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + public class Screening + { + public int Id { get; set; } + public int ScreenNumber { get; set; } + public DateTime StartsAt { get; set; } + public int Capacity { get; set; } + public int MovieId { get; set; } + + [Column("created_at", TypeName = "timestamp")] + public DateTime CreatedAt { get; set; } + [Column("updated_at", TypeName = "timestamp")] + public DateTime UpdatedAt { get; set; } + + public Movie Movie { get; set; } + public 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 new file mode 100644 index 00000000..7efb3cbc --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + public class Ticket + { + public int Id { get; set; } + public int NumSeats { get; set; } + public int CustomerId { get; set; } + public int ScreeningId { get; set; } + + [Column("created_at", TypeName = "timestamp")] + public DateTime CreatedAt { get; set; } + [Column("updated_at", TypeName = "timestamp")] + public DateTime UpdatedAt { get; set; } + + public Customer Customer { get; set; } + public Screening Screening { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..5ee05b47 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,11 +1,21 @@ 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); // Add services to the container. + +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddDbContext(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -builder.Services.AddDbContext(); +builder.Services.AddAutoMapper(typeof(Program)); var app = builder.Build(); @@ -17,4 +27,7 @@ } app.UseHttpsRedirection(); +app.ConfigureCinemaAPI(); +app.SeedDatabase(); + app.Run(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs new file mode 100644 index 00000000..8a4f0fe2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,17 @@ +using System.Linq.Expressions; + +namespace api_cinema_challenge.Repository +{ + public interface IRepository + { + Task GetById(int id); + Task> GetAll(); + Task Insert(T entity); + Task Delete(int id); + Task Update(T entity); + Task> GetWithIncludes(params Expression>[] includes); + Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions); + IQueryable GetQueryable(); + Task Save(); + } +} 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..d8131aee --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,84 @@ +using System.Linq.Expressions; +using api_cinema_challenge.Data; +using Microsoft.EntityFrameworkCore; + +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 GetById(int id) + { + return _table.Find(id); + } + + public async Task> GetAll() + { + return _table.ToList(); + } + + public async Task Insert(T entity) + { + _table.Add(entity); + await _db.SaveChangesAsync(); + return entity; + } + public async Task Update(T entity) + { + _table.Attach(entity); + _db.Entry(entity).State = EntityState.Modified; + await _db.SaveChangesAsync(); + return entity; + } + + public async Task Delete(int id) + { + T entity = _table.Find(id); + _table.Remove(entity); + await _db.SaveChangesAsync(); + return entity; + } + + public async Task> GetWithIncludes(params Expression>[] includes) + { + IQueryable query = _table; + + foreach (var include in includes) + { + query = query.Include(include); + } + + return await query.ToListAsync(); + } + + public async Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions) + { + IQueryable query = _table; + + foreach (var includeAction in includeActions) + { + query = includeAction(query); + } + + return await query.ToListAsync(); + } + + public IQueryable GetQueryable() + { + return _table; + } + + public async Task Save() + { + await _db.SaveChangesAsync(); + } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Request/CustomerPost.cs b/api-cinema-challenge/api-cinema-challenge/Request/CustomerPost.cs new file mode 100644 index 00000000..4b2e5e00 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Request/CustomerPost.cs @@ -0,0 +1,9 @@ +namespace api_cinema_challenge.Request +{ + public class CustomerPost + { + public string Name { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Request/MoviePost.cs b/api-cinema-challenge/api-cinema-challenge/Request/MoviePost.cs new file mode 100644 index 00000000..2c0e9e7e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Request/MoviePost.cs @@ -0,0 +1,14 @@ +using api_cinema_challenge.Models; + +namespace api_cinema_challenge.Request +{ + public class MoviePost + { + public string? Title { get; set; } + public string? Rating { get; set; } + public string? Description { get; set; } + public int? RuntimeMinutes { get; set; } + public List? screenings { get; set; } = new List(); + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Request/ScreeningPost.cs b/api-cinema-challenge/api-cinema-challenge/Request/ScreeningPost.cs new file mode 100644 index 00000000..1e2641e4 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Request/ScreeningPost.cs @@ -0,0 +1,9 @@ +namespace api_cinema_challenge.Request +{ + public class ScreeningPost + { + public int ScreenNumber { get; set; } + public DateTime StartsAt { get; set; } + public int Capacity { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Request/TicketPost.cs b/api-cinema-challenge/api-cinema-challenge/Request/TicketPost.cs new file mode 100644 index 00000000..43a52dee --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Request/TicketPost.cs @@ -0,0 +1,7 @@ +namespace api_cinema_challenge.Request +{ + public class TicketPost + { + public int NumSeats { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Response/Error.cs b/api-cinema-challenge/api-cinema-challenge/Response/Error.cs new file mode 100644 index 00000000..973dfd23 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Response/Error.cs @@ -0,0 +1,11 @@ +namespace api_cinema_challenge.Response +{ + public class Error + { + public string message { get; set; } + public Error(string message) + { + this.message = message; + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Response/ErrorResponse.cs b/api-cinema-challenge/api-cinema-challenge/Response/ErrorResponse.cs new file mode 100644 index 00000000..cd403eec --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Response/ErrorResponse.cs @@ -0,0 +1,11 @@ +using System.Transactions; + +namespace api_cinema_challenge.Response +{ + public class ErrorResponse : Response + { + public ErrorResponse(Error data) : base("Error", data) + { + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Response/Response.cs b/api-cinema-challenge/api-cinema-challenge/Response/Response.cs new file mode 100644 index 00000000..4fde5f6c --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Response/Response.cs @@ -0,0 +1,14 @@ +namespace api_cinema_challenge.Response +{ + public class Response + { + public string Status { get; set; } + public T Data { get; set; } + + public Response(string status, T data) + { + Status = status; + Data = data; + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Response/SuccessResponse.cs b/api-cinema-challenge/api-cinema-challenge/Response/SuccessResponse.cs new file mode 100644 index 00000000..c592d3a2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Response/SuccessResponse.cs @@ -0,0 +1,11 @@ +using System.Transactions; + +namespace api_cinema_challenge.Response +{ + public class SuccessResponse : Response + { + public SuccessResponse(Error data) : base("Success", data) + { + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs b/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs new file mode 100644 index 00000000..92b29159 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Tools/MappingProfile.cs @@ -0,0 +1,21 @@ +using api_cinema_challenge.DTOs; +using api_cinema_challenge.DTOs.NestedDTOs; +using api_cinema_challenge.Models; +using AutoMapper; + +namespace api_cinema_challenge.Tools +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + } + } +} 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..64ab3d0d 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 @@ + @@ -28,7 +29,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